@korso/shepherd-ui 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +47 -47
  2. package/dist/ShepherdRoot.js +15 -57
  3. package/dist/components/Dashboard.d.ts +16 -2
  4. package/dist/components/Dashboard.js +76 -5
  5. package/dist/config/EmptyState.js +5 -1
  6. package/dist/lib/{Dashboard-CG6KeFQ3.js → Dashboard-B7nioe5N.js} +529 -457
  7. package/dist/lib/index.d.ts +15 -1
  8. package/dist/lib/index.js +196 -279
  9. package/dist/lib/selfhost.js +1 -1
  10. package/dist/lib/styles.css +217 -209
  11. package/dist/selfhost/assets/index-D6rTsbVM.js +40 -0
  12. package/dist/selfhost/assets/{index-BZmImfYZ.css → index-DnhlP_lc.css} +1 -1
  13. package/dist/selfhost/index.html +13 -13
  14. package/dist/useLandscapePolling.d.ts +7 -0
  15. package/dist/useLandscapePolling.js +6 -1
  16. package/package.json +67 -67
  17. package/dist/ShepherdProvider.d.ts +0 -12
  18. package/dist/ShepherdProvider.js +0 -33
  19. package/dist/ShepherdProvider.test.d.ts +0 -1
  20. package/dist/ShepherdProvider.test.js +0 -47
  21. package/dist/ShepherdRoot.routing.test.d.ts +0 -1
  22. package/dist/ShepherdRoot.routing.test.js +0 -61
  23. package/dist/ShepherdRoot.test.d.ts +0 -1
  24. package/dist/ShepherdRoot.test.js +0 -37
  25. package/dist/app/assets/index-CDOCIg6s.js +0 -11
  26. package/dist/app/index.html +0 -12
  27. package/dist/client.test.d.ts +0 -1
  28. package/dist/client.test.js +0 -298
  29. package/dist/config/ConnectAgent.test.d.ts +0 -1
  30. package/dist/config/ConnectAgent.test.js +0 -92
  31. package/dist/config/EmptyState.test.d.ts +0 -1
  32. package/dist/config/EmptyState.test.js +0 -32
  33. package/dist/config/Members.test.d.ts +0 -1
  34. package/dist/config/Members.test.js +0 -66
  35. package/dist/config/Workspaces.test.d.ts +0 -1
  36. package/dist/config/Workspaces.test.js +0 -90
  37. package/dist/index.cjs +0 -5289
  38. package/dist/index.cjs.map +0 -1
  39. package/dist/index.d.cts +0 -149
  40. package/dist/index.js.map +0 -1
  41. package/dist/selfhost/assets/index-CS268KSw.js +0 -40
  42. package/dist/test/setup.d.ts +0 -1
  43. package/dist/test/setup.js +0 -2
  44. package/dist/views/Chat.d.ts +0 -5
  45. package/dist/views/Chat.js +0 -60
  46. package/dist/views/Chat.test.d.ts +0 -1
  47. package/dist/views/Chat.test.js +0 -92
  48. package/dist/views/Config.d.ts +0 -12
  49. package/dist/views/Config.js +0 -6
  50. package/dist/views/Tasks.d.ts +0 -5
  51. package/dist/views/Tasks.js +0 -75
  52. package/dist/views/Tasks.test.d.ts +0 -1
  53. package/dist/views/Tasks.test.js +0 -115
  54. package/dist/views/useLandscape.d.ts +0 -14
  55. package/dist/views/useLandscape.js +0 -74
  56. package/dist/views/useLandscape.test.d.ts +0 -1
  57. package/dist/views/useLandscape.test.js +0 -101
  58. package/dist/views/wallboard.d.ts +0 -46
  59. package/dist/views/wallboard.js +0 -209
  60. package/dist/views/wallboard.test.d.ts +0 -1
  61. package/dist/views/wallboard.test.js +0 -143
@@ -1 +1 @@
1
- :root{--bg:#ece0c6;--ink:#322c20;--ink2:#6f6450;--ink3:#9c917a;--line:#d7c9ac;--line2:#e0d4b9;--live:#3d8b58;--drop:#bd4f3a;--chip:#e3d6ba;--terr:#7a6a48;--card:#f5ecd9}*{box-sizing:border-box}html,body{height:100%}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}.wrap{max-width:1100px;margin:0 auto;padding:40px 44px}header{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--line)}#board:has(#panel-chat:not([hidden])) header{border-bottom:0}.brand{font-size:17px;font-weight:600}.vitals{color:var(--ink3);font-size:12.5px}.grow{flex:1}.status{font-size:12px;color:var(--ink3)}.status--ok{color:var(--live)}.status--warn{color:var(--terr)}.status--error{color:var(--drop)}.freshness{font-size:12px;color:var(--ink3)}.repo{position:relative}.repo-trig{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-family:inherit;color:var(--ink);-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid var(--line);border-radius:8px;padding:4px 10px;background:#fffaee66;cursor:pointer}.repo-trig .slash{color:var(--ink3)}.repo-menu{position:absolute;top:36px;left:0;min-width:200px;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:5px;box-shadow:0 8px 24px #32281424;z-index:5}.repo-mi{display:flex;justify-content:space-between;gap:14px;padding:7px 9px;width:100%;text-align:left;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;font-family:inherit;color:inherit;background:transparent;border-radius:7px;font-size:13px;cursor:pointer}.repo-mi:hover{background:var(--chip)}.repo-mi.on{background:var(--chip);font-weight:600}.repo-mi .ct{font-size:11px;color:var(--ink3)}.repo-mi.all{border-top:1px solid var(--line2);margin-top:4px;padding-top:9px;color:var(--ink2)}.tabs{display:flex;gap:4px;margin-left:auto}.tab{font-size:13px;color:var(--ink2);padding:5px 11px;border:0;border-radius:7px;background:transparent;cursor:pointer;font:inherit}.tab--active{background:var(--ink);color:var(--bg)}.crew{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:26px 0 32px}.person{display:inline-flex;align-items:center;gap:9px;padding:5px 13px 5px 5px;border:1px solid var(--line);border-radius:999px;background:var(--card);font-size:13px}.person--idle{opacity:.55}.avatar{width:24px;height:24px;border-radius:50%;display:grid;place-items:center;font-size:10px;font-weight:700;color:#fff;flex:none}.person__name{font-weight:600}.person__doing{color:var(--ink3);font-size:12px}.board{display:grid;grid-template-columns:1fr 1px 1fr;gap:0}.board__rule{background:var(--line)}.col{padding:0 36px}.col:first-child{padding-left:0}.col:last-child{padding-right:0}.colhead{display:flex;align-items:center;gap:8px;margin-bottom:6px}.colhead h2{font-size:12px;font-weight:600;letter-spacing:.03em;margin:0;text-transform:uppercase;color:var(--ink2)}.colhead .n{font-size:11px;color:var(--ink3);background:var(--chip);border-radius:999px;padding:1px 8px}.task{padding:16px 0;border-bottom:1px solid var(--line2)}.task__r1{display:flex;align-items:center;gap:8px}.task__who{font-size:13.5px;font-weight:600}.task__tag{font-size:11px;color:var(--ink3)}.task__repo{font-size:11px;color:var(--terr);background:#7a6a481a;border-radius:4px;padding:0 6px}.task__stat{margin-left:auto;font-size:11px;font-weight:500;color:var(--ink3)}.task__stat--drop{color:var(--drop)}.livedot{width:7px;height:7px;border-radius:50%;background:var(--live);margin-left:auto}.task__intent{font-size:13.5px;margin:5px 0 0;line-height:1.45}.terr{margin-top:8px;display:flex;flex-wrap:wrap;gap:6px;align-items:center}.terr__lbl{font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink3)}.glob{font-family:ui-monospace,Menlo,monospace;font-size:11px;color:var(--terr);background:#7a6a481f;border:1px solid rgba(122,106,72,.22);border-radius:5px;padding:1px 7px}.task__meta{font-size:11.5px;color:var(--ink3);margin-top:6px}.terrx{margin-top:8px}.terrx summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:6px;flex-wrap:wrap;-webkit-user-select:none;user-select:none}.terrx summary::-webkit-details-marker{display:none}.terrx .terr--full{margin-top:8px}.glob--more{cursor:pointer;color:var(--ink2);background:var(--chip);border:1px solid var(--line)}.glob--more .tw{color:var(--ink3);display:inline-block;transition:transform .15s}.terrx[open] summary .glob--more .tw{transform:rotate(90deg)}.grp{padding:16px 0;border-bottom:1px solid var(--line2)}.grp__head{display:flex;align-items:center;gap:8px}.grp__who{font-size:13.5px;font-weight:600}.grp__tag,.grp__count{font-size:11px;color:var(--ink3)}.grp__dot{width:7px;height:7px;border-radius:50%;background:var(--live);margin-left:auto}.claims{margin:10px 0 0 6px;padding-left:14px;border-left:1px solid var(--line)}.claim{padding:6px 0}.claim+.claim{border-top:1px solid var(--line2);margin-top:6px}.fold{margin-top:10px}.fold summary{list-style:none;cursor:pointer;font-size:12px;color:var(--ink2);display:inline-flex;align-items:center;gap:6px;-webkit-user-select:none;user-select:none}.fold summary::-webkit-details-marker{display:none}.fold summary .tw{display:inline-block;color:var(--ink3);transition:transform .15s}.fold[open] summary .tw{transform:rotate(90deg)}.fold__body{margin:8px 0 0 6px;padding-left:14px;border-left:1px dashed var(--line)}.covered{font-size:10.5px;color:var(--ink3);font-style:italic;margin-top:4px}.day{font-size:11px;color:var(--ink3);padding:18px 0 2px;font-weight:600}.more{margin-top:20px;font-size:12.5px;color:var(--ink2);padding:7px 13px;border:1px solid var(--line);border-radius:8px;background:#fffaee59;cursor:pointer}.empty{color:var(--ink3);font-style:italic;padding:14px 0}.chat-wrap{background:var(--card);border:1px solid var(--line);border-radius:12px;display:flex;flex-direction:column;overflow:hidden;height:calc(100vh - 150px);min-height:320px}.chat{flex:1;overflow-y:auto;padding:16px}.chat{scrollbar-width:thin;scrollbar-color:var(--line) transparent}.chat::-webkit-scrollbar{width:10px}.chat::-webkit-scrollbar-track{background:transparent}.chat::-webkit-scrollbar-thumb{background:var(--line);border-radius:999px;border:3px solid var(--card);background-clip:padding-box}.chat::-webkit-scrollbar-thumb:hover{background:var(--ink3);background-clip:padding-box}.chat__note{padding:9px 16px;font-size:11.5px;color:var(--ink3)}.msg{display:flex;gap:11px;padding:9px 0}.msg__avatar{flex:none;width:32px;height:32px;border-radius:50%;display:grid;place-items:center;font-size:12px;font-weight:700;color:#fff}.msg__body{min-width:0;flex:1}.msg__head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}.msg__who{font-weight:600}.msg__human{font-size:12px;color:var(--ink3)}.msg__to{font-size:11.5px;color:var(--terr)}.msg__time{font-size:11.5px;color:var(--ink3);margin-left:auto}.msg__text{margin-top:3px;padding:8px 12px;border-radius:4px 12px 12px;background:var(--chip);white-space:pre-wrap;word-break:break-word}.msg--targeted .msg__text{box-shadow:inset 3px 0 0 var(--terr)}.msg--me,.msg--me .msg__head{flex-direction:row-reverse}.msg--me .msg__time{margin-left:0;margin-right:auto}.msg--me .msg__body{display:flex;flex-direction:column;align-items:flex-end}.msg--me .msg__text{border-radius:12px 4px 12px 12px;background:#d4e7d2}.msg--me.msg--targeted .msg__text{box-shadow:inset -3px 0 0 var(--terr)}.composer{position:relative;border-top:1px solid var(--line);padding:10px 12px}.chat-form{display:flex;gap:8px}.chat-input{flex:1;padding:9px 12px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);font:inherit}.chat-input:focus{outline:none;border-color:var(--terr)}.chat-send{padding:9px 16px;border-radius:8px;border:1px solid var(--ink);background:var(--ink);color:var(--bg);font-weight:600;cursor:pointer;font:inherit}.chat-send:disabled{opacity:.5;cursor:default}.mention-pop{position:absolute;left:12px;right:12px;bottom:56px;z-index:6;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:5px;box-shadow:0 8px 24px #32281429;max-height:200px;overflow-y:auto}.mention-mi{display:flex;align-items:center;gap:9px;width:100%;text-align:left;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;font-family:inherit;color:inherit;background:transparent;border-radius:7px;padding:6px 8px;font-size:13px;cursor:pointer}.mention-mi:hover,.mention-mi.on{background:var(--chip)}.mention-mi .ma{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;font-size:9px;font-weight:700;color:#fff;flex:none}.gate{max-width:420px;margin:12vh auto 0;text-align:center}.gate h1{font-size:19px;margin:0 0 6px}.gate p{color:var(--ink2);margin:0 0 20px}.gate form{display:flex;gap:8px}.gate input{flex:1;padding:10px 12px;border-radius:8px;border:1px solid var(--line);background:var(--card);color:var(--ink)}.gate button{padding:10px 16px;border-radius:8px;border:1px solid var(--ink);background:var(--ink);color:var(--bg);font-weight:600;cursor:pointer}.shepherd-root{max-width:1100px;margin:0 auto;padding:40px 44px}.shepherd-root>main{padding-top:18px}.shepherd-config{display:flex;flex-direction:column;gap:24px}.shepherd-config h2{font-size:16px;margin:0 0 4px}.shepherd-workspaces,.shepherd-members,.shepherd-connect-agent{display:flex;flex-direction:column;gap:10px;padding:16px;border:1px solid var(--line);border-radius:10px;background:var(--card)}.shepherd-workspaces .switcher{display:flex;flex-wrap:wrap;gap:8px}.shepherd-workspaces .role{color:var(--ink3);font-size:12px}.shepherd-workspaces .admin,.shepherd-workspaces .invites{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.shepherd-config input,.shepherd-config select{padding:8px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink)}.shepherd-config button{padding:8px 14px;border-radius:8px;border:1px solid var(--ink);background:var(--ink);color:var(--bg);font-weight:600;cursor:pointer}.shepherd-config button:disabled{opacity:.55;cursor:default}.shepherd-config .invite-result,.shepherd-connect-agent .token-once{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;word-break:break-all;padding:8px 10px;border-radius:8px;background:var(--chip);border:1px solid var(--line2)}.shepherd-config .revoked{color:var(--ink3);text-decoration:line-through}.shepherd-empty-state{text-align:center;color:var(--ink2);padding:48px 24px}.shepherd-empty-state button{margin-top:14px}
1
+ :root{--bg:#ece0c6;--ink:#322c20;--ink2:#6f6450;--ink3:#9c917a;--line:#d7c9ac;--line2:#e0d4b9;--live:#3d8b58;--drop:#bd4f3a;--chip:#e3d6ba;--terr:#7a6a48;--card:#f5ecd9}*{box-sizing:border-box}html,body{height:100%}body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}.wrap{max-width:1100px;margin:0 auto;padding:40px 44px}header{display:flex;align-items:center;gap:12px;padding-bottom:18px;border-bottom:1px solid var(--line)}#board:has(#panel-chat:not([hidden])) header{border-bottom:0}.brand{font-size:17px;font-weight:600}.vitals{color:var(--ink3);font-size:12.5px}.grow{flex:1}.status{font-size:12px;color:var(--ink3)}.status--ok{color:var(--live)}.status--warn{color:var(--terr)}.status--error{color:var(--drop)}.freshness{font-size:12px;color:var(--ink3)}.repo{position:relative}.repo-trig{display:inline-flex;align-items:center;gap:6px;font-size:13px;font-family:inherit;color:var(--ink);-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid var(--line);border-radius:8px;padding:4px 10px;background:#fffaee66;cursor:pointer}.repo-trig .slash{color:var(--ink3)}.repo-menu{position:absolute;top:36px;left:0;min-width:200px;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:5px;box-shadow:0 8px 24px #32281424;z-index:5}.repo-mi{display:flex;justify-content:space-between;gap:14px;padding:7px 9px;width:100%;text-align:left;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;font-family:inherit;color:inherit;background:transparent;border-radius:7px;font-size:13px;cursor:pointer}.repo-mi:hover{background:var(--chip)}.repo-mi.on{background:var(--chip);font-weight:600}.repo-mi .ct{font-size:11px;color:var(--ink3)}.repo-mi.all{border-top:1px solid var(--line2);margin-top:4px;padding-top:9px;color:var(--ink2)}.tabs{display:flex;gap:4px;margin-left:auto}.tab{font-size:13px;color:var(--ink2);padding:5px 11px;border:0;border-radius:7px;background:transparent;cursor:pointer;font:inherit}.tab--active{background:var(--ink);color:var(--bg)}.crew{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:26px 0 32px}.person{display:inline-flex;align-items:center;gap:9px;padding:5px 13px 5px 5px;border:1px solid var(--line);border-radius:999px;background:var(--card);font-size:13px}.person--idle{opacity:.55}.avatar{width:24px;height:24px;border-radius:50%;display:grid;place-items:center;font-size:10px;font-weight:700;color:#fff;flex:none}.person__name{font-weight:600}.person__doing{color:var(--ink3);font-size:12px}.board{display:grid;grid-template-columns:1fr 1px 1fr;gap:0}.board__rule{background:var(--line)}.col{padding:0 36px}.col:first-child{padding-left:0}.col:last-child{padding-right:0}.colhead{display:flex;align-items:center;gap:8px;margin-bottom:6px}.colhead h2{font-size:12px;font-weight:600;letter-spacing:.03em;margin:0;text-transform:uppercase;color:var(--ink2)}.colhead .n{font-size:11px;color:var(--ink3);background:var(--chip);border-radius:999px;padding:1px 8px}.task{padding:16px 0;border-bottom:1px solid var(--line2)}.task__r1{display:flex;align-items:center;gap:8px}.task__who{font-size:13.5px;font-weight:600}.task__tag{font-size:11px;color:var(--ink3)}.task__repo{font-size:11px;color:var(--terr);background:#7a6a481a;border-radius:4px;padding:0 6px}.task__stat{margin-left:auto;font-size:11px;font-weight:500;color:var(--ink3)}.task__stat--drop{color:var(--drop)}.livedot{width:7px;height:7px;border-radius:50%;background:var(--live);margin-left:auto}.task__intent{font-size:13.5px;margin:5px 0 0;line-height:1.45}.terr{margin-top:8px;display:flex;flex-wrap:wrap;gap:6px;align-items:center}.terr__lbl{font-size:9.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink3)}.glob{font-family:ui-monospace,Menlo,monospace;font-size:11px;color:var(--terr);background:#7a6a481f;border:1px solid rgba(122,106,72,.22);border-radius:5px;padding:1px 7px}.task__meta{font-size:11.5px;color:var(--ink3);margin-top:6px}.terrx{margin-top:8px}.terrx summary{list-style:none;cursor:pointer;display:flex;align-items:center;gap:6px;flex-wrap:wrap;-webkit-user-select:none;user-select:none}.terrx summary::-webkit-details-marker{display:none}.terrx .terr--full{margin-top:8px}.glob--more{cursor:pointer;color:var(--ink2);background:var(--chip);border:1px solid var(--line)}.glob--more .tw{color:var(--ink3);display:inline-block;transition:transform .15s}.terrx[open] summary .glob--more .tw{transform:rotate(90deg)}.grp{padding:16px 0;border-bottom:1px solid var(--line2)}.grp__head{display:flex;align-items:center;gap:8px}.grp__who{font-size:13.5px;font-weight:600}.grp__tag,.grp__count{font-size:11px;color:var(--ink3)}.grp__dot{width:7px;height:7px;border-radius:50%;background:var(--live);margin-left:auto}.claims{margin:10px 0 0 6px;padding-left:14px;border-left:1px solid var(--line)}.claim{padding:6px 0}.claim+.claim{border-top:1px solid var(--line2);margin-top:6px}.fold{margin-top:10px}.fold summary{list-style:none;cursor:pointer;font-size:12px;color:var(--ink2);display:inline-flex;align-items:center;gap:6px;-webkit-user-select:none;user-select:none}.fold summary::-webkit-details-marker{display:none}.fold summary .tw{display:inline-block;color:var(--ink3);transition:transform .15s}.fold[open] summary .tw{transform:rotate(90deg)}.fold__body{margin:8px 0 0 6px;padding-left:14px;border-left:1px dashed var(--line)}.covered{font-size:10.5px;color:var(--ink3);font-style:italic;margin-top:4px}.day{font-size:11px;color:var(--ink3);padding:18px 0 2px;font-weight:600}.more{margin-top:20px;font-size:12.5px;color:var(--ink2);padding:7px 13px;border:1px solid var(--line);border-radius:8px;background:#fffaee59;cursor:pointer}.empty{color:var(--ink3);font-style:italic;padding:14px 0}.chat-wrap{background:var(--card);border:1px solid var(--line);border-radius:12px;display:flex;flex-direction:column;overflow:hidden;height:calc(100vh - 150px);min-height:320px}.chat{flex:1;overflow-y:auto;padding:16px}.chat{scrollbar-width:thin;scrollbar-color:var(--line) transparent}.chat::-webkit-scrollbar{width:10px}.chat::-webkit-scrollbar-track{background:transparent}.chat::-webkit-scrollbar-thumb{background:var(--line);border-radius:999px;border:3px solid var(--card);background-clip:padding-box}.chat::-webkit-scrollbar-thumb:hover{background:var(--ink3);background-clip:padding-box}.chat__note{padding:9px 16px;font-size:11.5px;color:var(--ink3)}.msg{display:flex;gap:11px;padding:9px 0}.msg__avatar{flex:none;width:32px;height:32px;border-radius:50%;display:grid;place-items:center;font-size:12px;font-weight:700;color:#fff}.msg__body{min-width:0;flex:1}.msg__head{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}.msg__who{font-weight:600}.msg__human{font-size:12px;color:var(--ink3)}.msg__to{font-size:11.5px;color:var(--terr)}.msg__time{font-size:11.5px;color:var(--ink3);margin-left:auto}.msg__text{margin-top:3px;padding:8px 12px;border-radius:4px 12px 12px;background:var(--chip);white-space:pre-wrap;word-break:break-word}.msg--targeted .msg__text{box-shadow:inset 3px 0 0 var(--terr)}.msg--me,.msg--me .msg__head{flex-direction:row-reverse}.msg--me .msg__time{margin-left:0;margin-right:auto}.msg--me .msg__body{display:flex;flex-direction:column;align-items:flex-end}.msg--me .msg__text{border-radius:12px 4px 12px 12px;background:#d4e7d2}.msg--me.msg--targeted .msg__text{box-shadow:inset -3px 0 0 var(--terr)}.composer{position:relative;border-top:1px solid var(--line);padding:10px 12px}.chat-form{display:flex;gap:8px}.chat-input{flex:1;padding:9px 12px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink);font:inherit}.chat-input:focus{outline:none;border-color:var(--terr)}.chat-send{padding:9px 16px;border-radius:8px;border:1px solid var(--ink);background:var(--ink);color:var(--bg);font-weight:600;cursor:pointer;font:inherit}.chat-send:disabled{opacity:.5;cursor:default}.mention-pop{position:absolute;left:12px;right:12px;bottom:56px;z-index:6;background:var(--card);border:1px solid var(--line);border-radius:10px;padding:5px;box-shadow:0 8px 24px #32281429;max-height:200px;overflow-y:auto}.mention-mi{display:flex;align-items:center;gap:9px;width:100%;text-align:left;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;font-family:inherit;color:inherit;background:transparent;border-radius:7px;padding:6px 8px;font-size:13px;cursor:pointer}.mention-mi:hover,.mention-mi.on{background:var(--chip)}.mention-mi .ma{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;font-size:9px;font-weight:700;color:#fff;flex:none}.gate{max-width:420px;margin:12vh auto 0;text-align:center}.gate h1{font-size:19px;margin:0 0 6px}.gate p{color:var(--ink2);margin:0 0 20px}.gate form{display:flex;gap:8px}.gate input{flex:1;padding:10px 12px;border-radius:8px;border:1px solid var(--line);background:var(--card);color:var(--ink)}.gate button{padding:10px 16px;border-radius:8px;border:1px solid var(--ink);background:var(--ink);color:var(--bg);font-weight:600;cursor:pointer}.shepherd-root{max-width:1100px;margin:0 auto;padding:40px 44px}.shepherd-root>main{padding-top:18px}.shepherd-config{display:flex;flex-direction:column;gap:24px}.shepherd-config h2{font-size:16px;margin:0 0 4px}.shepherd-workspaces,.shepherd-members,.shepherd-connect-agent{display:flex;flex-direction:column;gap:10px;padding:16px;border:1px solid var(--line);border-radius:10px;background:var(--card)}.shepherd-workspaces .switcher{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.shepherd-workspaces .role{color:var(--ink3);font-size:12px}.shepherd-workspaces .admin,.shepherd-workspaces .invites,.shepherd-workspaces .create,.shepherd-workspaces .join{display:flex;flex-wrap:wrap;gap:8px;align-items:center}.shepherd-workspaces .create>label,.shepherd-workspaces .join>label,.shepherd-workspaces .switcher>label{min-width:104px;color:var(--ink2)}.shepherd-workspaces .create>input,.shepherd-workspaces .join>input{flex:1;min-width:180px}.shepherd-config input,.shepherd-config select{padding:8px 10px;border-radius:8px;border:1px solid var(--line);background:var(--bg);color:var(--ink)}.shepherd-config button{padding:8px 14px;border-radius:8px;border:1px solid var(--ink);background:var(--ink);color:var(--bg);font-weight:600;cursor:pointer}.shepherd-config button:disabled{opacity:.55;cursor:default}.shepherd-config .invite-result,.shepherd-connect-agent .token-once{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;word-break:break-all;padding:8px 10px;border-radius:8px;background:var(--chip);border:1px solid var(--line2)}.shepherd-config .revoked{color:var(--ink3);text-decoration:line-through}.shepherd-empty-state{text-align:center;color:var(--ink2);padding:48px 24px}.shepherd-empty-state button{margin-top:14px}
@@ -1,13 +1,13 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>Shepherd</title>
7
- <script type="module" crossorigin src="/assets/index-CS268KSw.js"></script>
8
- <link rel="stylesheet" crossorigin href="/assets/index-BZmImfYZ.css">
9
- </head>
10
- <body>
11
- <div id="root"></div>
12
- </body>
13
- </html>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Shepherd</title>
7
+ <script type="module" crossorigin src="/assets/index-D6rTsbVM.js"></script>
8
+ <link rel="stylesheet" crossorigin href="/assets/index-DnhlP_lc.css">
9
+ </head>
10
+ <body>
11
+ <div id="root"></div>
12
+ </body>
13
+ </html>
@@ -17,6 +17,13 @@ export interface UseLandscapePollingOptions {
17
17
  workspaceId?: string;
18
18
  /** Poll cadence in ms; defaults to {@link DEFAULT_POLL_MS} (5000). */
19
19
  pollMs?: number;
20
+ /**
21
+ * Whether to poll at all; defaults to `true`. When `false` the hook performs
22
+ * NO request (no initial poll, no interval) and stays on its last value — used
23
+ * by the hosted shell to keep the board from polling when there is no
24
+ * workspace to scope to.
25
+ */
26
+ enabled?: boolean;
20
27
  }
21
28
  /** The reactive value returned by {@link useLandscapePolling}. */
22
29
  export interface LandscapePolling {
@@ -40,6 +40,7 @@ export function useLandscapePolling(opts) {
40
40
  const client = useShepherdClient();
41
41
  const workspaceId = opts?.workspaceId;
42
42
  const pollMs = opts?.pollMs ?? DEFAULT_POLL_MS;
43
+ const enabled = opts?.enabled ?? true;
43
44
  const [snapshot, setSnapshot] = useState(null);
44
45
  const [status, setStatus] = useState("live");
45
46
  const [lastUpdatedMs, setLastUpdatedMs] = useState(null);
@@ -98,6 +99,10 @@ export function useLandscapePolling(opts) {
98
99
  // return to visibility polls right away to catch up. The 1s freshness interval
99
100
  // is independent so a slow/failed poll never blocks the relative-time refresh.
100
101
  useEffect(() => {
102
+ // Disabled: do nothing — no poll, no intervals — so a no-workspace board
103
+ // never hits the hub. Re-enabling re-runs this effect and pulls immediately.
104
+ if (!enabled)
105
+ return;
101
106
  const myGen = ++gen.current;
102
107
  void poll(myGen);
103
108
  const pollTimer = setInterval(() => {
@@ -121,7 +126,7 @@ export function useLandscapePolling(opts) {
121
126
  clearInterval(freshnessTimer);
122
127
  document.removeEventListener("visibilitychange", onVisible);
123
128
  };
124
- }, [poll, pollMs]);
129
+ }, [poll, pollMs, enabled]);
125
130
  // Manual refresh always runs under the current generation, so its result
126
131
  // applies as long as the hook hasn't since switched workspace or unmounted.
127
132
  const refresh = useCallback(() => poll(gen.current), [poll]);
package/package.json CHANGED
@@ -1,67 +1,67 @@
1
- {
2
- "name": "@korso/shepherd-ui",
3
- "version": "0.2.0",
4
- "description": "Auth-agnostic React UI library for the Shepherd wallboard (Dashboard, client, provider). Host injects auth; the library never reads tokens.",
5
- "type": "module",
6
- "license": "UNLICENSED",
7
- "publishConfig": {
8
- "access": "public"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/Korsoai/shepherd.git",
13
- "directory": "packages/ui"
14
- },
15
- "exports": {
16
- ".": {
17
- "import": "./dist/lib/index.js",
18
- "types": "./dist/lib/index.d.ts"
19
- },
20
- "./selfhost": {
21
- "import": "./dist/lib/selfhost.js",
22
- "types": "./dist/lib/selfhost.d.ts"
23
- },
24
- "./styles.css": "./dist/lib/styles.css",
25
- "./package.json": "./package.json"
26
- },
27
- "types": "./dist/lib/index.d.ts",
28
- "files": [
29
- "dist",
30
- "README.md"
31
- ],
32
- "scripts": {
33
- "build": "npm run build:lib && npm run build:app",
34
- "build:lib": "vite build --config vite.config.lib.ts && npm run copy:styles",
35
- "build:app": "vite build --config vite.config.app.ts",
36
- "copy:styles": "node -e \"const fs=require('node:fs'); if (fs.existsSync('src/styles.css')) { fs.mkdirSync('dist/lib', { recursive:true }); fs.copyFileSync('src/styles.css','dist/lib/styles.css'); }\"",
37
- "dev": "vite --config vite.config.app.ts",
38
- "preview": "vite preview --config vite.config.app.ts",
39
- "type-check": "tsc --noEmit",
40
- "test": "vitest run",
41
- "prepublishOnly": "npm run build:lib"
42
- },
43
- "peerDependencies": {
44
- "react": "^18 || ^19",
45
- "react-dom": "^18 || ^19"
46
- },
47
- "dependencies": {
48
- "zod": "^3"
49
- },
50
- "devDependencies": {
51
- "@microsoft/api-extractor": "^7",
52
- "@shepherd/shared": "*",
53
- "@testing-library/jest-dom": "latest",
54
- "@testing-library/react": "latest",
55
- "@testing-library/user-event": "latest",
56
- "@types/react": "^18",
57
- "@types/react-dom": "^18",
58
- "@vitejs/plugin-react": "^4",
59
- "jsdom": "latest",
60
- "react": "^18",
61
- "react-dom": "^18",
62
- "typescript": "^5",
63
- "vite": "^6",
64
- "vite-plugin-dts": "^5",
65
- "vitest": "^4"
66
- }
67
- }
1
+ {
2
+ "name": "@korso/shepherd-ui",
3
+ "version": "0.2.1",
4
+ "description": "Auth-agnostic React UI library for the Shepherd wallboard (Dashboard, client, provider). Host injects auth; the library never reads tokens.",
5
+ "type": "module",
6
+ "license": "UNLICENSED",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/Korsoai/shepherd.git",
13
+ "directory": "packages/ui"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/lib/index.js",
18
+ "types": "./dist/lib/index.d.ts"
19
+ },
20
+ "./selfhost": {
21
+ "import": "./dist/lib/selfhost.js",
22
+ "types": "./dist/lib/selfhost.d.ts"
23
+ },
24
+ "./styles.css": "./dist/lib/styles.css",
25
+ "./package.json": "./package.json"
26
+ },
27
+ "types": "./dist/lib/index.d.ts",
28
+ "files": [
29
+ "dist",
30
+ "README.md"
31
+ ],
32
+ "scripts": {
33
+ "build": "npm run build:lib && npm run build:app",
34
+ "build:lib": "vite build --config vite.config.lib.ts && npm run copy:styles",
35
+ "build:app": "vite build --config vite.config.app.ts",
36
+ "copy:styles": "node -e \"const fs=require('node:fs'); if (fs.existsSync('src/styles.css')) { fs.mkdirSync('dist/lib', { recursive:true }); fs.copyFileSync('src/styles.css','dist/lib/styles.css'); }\"",
37
+ "dev": "vite --config vite.config.app.ts",
38
+ "preview": "vite preview --config vite.config.app.ts",
39
+ "type-check": "tsc --noEmit",
40
+ "test": "vitest run",
41
+ "prepublishOnly": "npm run build:lib"
42
+ },
43
+ "peerDependencies": {
44
+ "react": "^18 || ^19",
45
+ "react-dom": "^18 || ^19"
46
+ },
47
+ "dependencies": {
48
+ "zod": "^3"
49
+ },
50
+ "devDependencies": {
51
+ "@microsoft/api-extractor": "^7",
52
+ "@shepherd/shared": "*",
53
+ "@testing-library/jest-dom": "latest",
54
+ "@testing-library/react": "latest",
55
+ "@testing-library/user-event": "latest",
56
+ "@types/react": "^18",
57
+ "@types/react-dom": "^18",
58
+ "@vitejs/plugin-react": "^4",
59
+ "jsdom": "latest",
60
+ "react": "^18",
61
+ "react-dom": "^18",
62
+ "typescript": "^5",
63
+ "vite": "^6",
64
+ "vite-plugin-dts": "^5",
65
+ "vitest": "^4"
66
+ }
67
+ }
@@ -1,12 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import type { ShepherdClient, ShepherdClientConfig } from "./client.js";
3
- export interface ShepherdProviderProps {
4
- /** A pre-built client. Mutually exclusive with `config`. */
5
- client?: ShepherdClient;
6
- /** Config to build a client from, when one is not supplied directly. */
7
- config?: ShepherdClientConfig;
8
- children: ReactNode;
9
- }
10
- export declare function ShepherdProvider({ client, config, children }: ShepherdProviderProps): import("react").JSX.Element;
11
- /** Access the ShepherdClient from context. Throws if used outside a provider. */
12
- export declare function useShepherdClient(): ShepherdClient;
@@ -1,33 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { createContext, useContext, useMemo } from "react";
3
- import { createShepherdClient } from "./client.js";
4
- // ---------------------------------------------------------------------------
5
- // ShepherdProvider — supplies a ShepherdClient to the view tree via context.
6
- //
7
- // Auth-agnostic: the provider takes either a ready-made `client` or a `config`
8
- // (baseUrl + optional getAuthHeader) and builds one. It imports no auth library.
9
- // ---------------------------------------------------------------------------
10
- const ShepherdContext = createContext(null);
11
- export function ShepherdProvider({ client, config, children }) {
12
- // Memoise on STABLE PRIMITIVE deps (baseUrl + getAuthHeader) rather than the
13
- // whole `config` object. A consumer passing an inline `config={{ … }}` mints a
14
- // new object every render; keying on it would rebuild the client each render and
15
- // re-fire every client-dep effect (e.g. re-subscribing useLandscape's 5s poll).
16
- // A directly-supplied `client` still takes precedence.
17
- const baseUrl = config?.baseUrl;
18
- const getAuthHeader = config?.getAuthHeader;
19
- const value = useMemo(() => {
20
- if (client)
21
- return client;
22
- return createShepherdClient({ baseUrl: baseUrl ?? "", getAuthHeader });
23
- }, [client, baseUrl, getAuthHeader]);
24
- return _jsx(ShepherdContext.Provider, { value: value, children: children });
25
- }
26
- /** Access the ShepherdClient from context. Throws if used outside a provider. */
27
- export function useShepherdClient() {
28
- const client = useContext(ShepherdContext);
29
- if (!client) {
30
- throw new Error("useShepherdClient must be used within a <ShepherdProvider>.");
31
- }
32
- return client;
33
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,47 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { describe, it, expect, vi, afterEach } from "vitest";
3
- import { render, renderHook, act } from "@testing-library/react";
4
- import { ShepherdProvider, useShepherdClient } from "./ShepherdProvider.js";
5
- // ---------------------------------------------------------------------------
6
- // ShepherdProvider — supplies the client via context. Covers the throw-outside-
7
- // provider guard and the stable-deps memoisation (P2.5): an inline `config`
8
- // object must not rebuild the client every render.
9
- // ---------------------------------------------------------------------------
10
- describe("useShepherdClient", () => {
11
- afterEach(() => {
12
- vi.restoreAllMocks();
13
- });
14
- it("throws a clear error when used outside a <ShepherdProvider>", () => {
15
- // Silence the expected React error-boundary console noise.
16
- const spy = vi.spyOn(console, "error").mockImplementation(() => { });
17
- expect(() => renderHook(() => useShepherdClient())).toThrow(/must be used within a <ShepherdProvider>/);
18
- spy.mockRestore();
19
- });
20
- it("returns the same client across renders when config props are stable", () => {
21
- const getAuthHeader = () => "Bearer t";
22
- const baseUrl = "https://hub.example.com";
23
- function wrapper({ children }) {
24
- // A NEW inline config object each render — identity changes every time.
25
- return (_jsx(ShepherdProvider, { config: { baseUrl, getAuthHeader }, children: children }));
26
- }
27
- const { result, rerender } = renderHook(() => useShepherdClient(), { wrapper });
28
- const first = result.current;
29
- rerender();
30
- // Memoised on primitive deps (baseUrl + getAuthHeader), so the client is
31
- // stable even though the `config` object identity changed.
32
- expect(result.current).toBe(first);
33
- });
34
- it("prefers a directly-supplied client over config", () => {
35
- const client = { baseUrl: "x" };
36
- const config = { baseUrl: "ignored" };
37
- let seen;
38
- function Probe() {
39
- seen = useShepherdClient();
40
- return null;
41
- }
42
- act(() => {
43
- render(_jsx(ShepherdProvider, { client: client, config: config, children: _jsx(Probe, {}) }));
44
- });
45
- expect(seen).toBe(client);
46
- });
47
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,61 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { describe, it, expect, vi, beforeEach } from "vitest";
3
- import { render, screen, waitFor } from "@testing-library/react";
4
- import { userEvent } from "@testing-library/user-event";
5
- import { ShepherdProvider } from "./ShepherdProvider.js";
6
- import { ShepherdRoot } from "./ShepherdRoot.js";
7
- import { makeMockClient } from "./test/mockClient.js";
8
- // ---------------------------------------------------------------------------
9
- // ShepherdRoot — routing / landing + empty-state gating (Task 6.3).
10
- //
11
- // The route guard keys off listWorkspaces() being empty:
12
- // - no workspace → land on Config; Tasks/Chat render an EmptyState prompt.
13
- // - >= 1 workspace → land on Tasks for the first (selected) workspace.
14
- // ---------------------------------------------------------------------------
15
- const WS = { id: "ws_1", slug: "acme", name: "Acme", role: "admin" };
16
- describe("ShepherdRoot routing / landing", () => {
17
- let client;
18
- beforeEach(() => {
19
- client = makeMockClient();
20
- });
21
- function renderRoot() {
22
- return render(_jsx(ShepherdProvider, { client: client, children: _jsx(ShepherdRoot, { hubUrl: "https://hub.example.run.app" }) }));
23
- }
24
- it("lands on Tasks when the account has at least one workspace", async () => {
25
- client.listWorkspaces = vi.fn().mockResolvedValue({ workspaces: [WS] });
26
- renderRoot();
27
- await waitFor(() => expect(screen.getByRole("tab", { name: "Tasks" })).toHaveAttribute("aria-selected", "true"));
28
- expect(screen.getByRole("heading", { name: "Tasks" })).toBeInTheDocument();
29
- });
30
- it("passes the selected workspaceId into the Tasks view", async () => {
31
- client.listWorkspaces = vi.fn().mockResolvedValue({ workspaces: [WS] });
32
- renderRoot();
33
- await waitFor(() => expect(screen.getByTestId("tasks-workspace-id")).toHaveTextContent("ws_1"));
34
- });
35
- it("lands on Config when the account has no workspace", async () => {
36
- client.listWorkspaces = vi.fn().mockResolvedValue({ workspaces: [] });
37
- renderRoot();
38
- await waitFor(() => expect(screen.getByRole("tab", { name: "Config" })).toHaveAttribute("aria-selected", "true"));
39
- });
40
- it("shows an EmptyState (not an error) on Tasks when there is no workspace", async () => {
41
- client.listWorkspaces = vi.fn().mockResolvedValue({ workspaces: [] });
42
- renderRoot();
43
- // Wait for the load to resolve (Config becomes active).
44
- await waitFor(() => expect(screen.getByRole("tab", { name: "Config" })).toHaveAttribute("aria-selected", "true"));
45
- await userEvent.click(screen.getByRole("tab", { name: "Tasks" }));
46
- // The empty-state prompt renders, never a hard wall / crash.
47
- expect(screen.getByRole("heading", { name: /no workspace yet|get started/i })).toBeInTheDocument();
48
- // The real wallboard scaffold heading is NOT present.
49
- expect(screen.queryByTestId("tasks-workspace-id")).not.toBeInTheDocument();
50
- });
51
- it("shows a loading indicator before the workspace list resolves", async () => {
52
- let resolve;
53
- client.listWorkspaces = vi.fn().mockReturnValue(new Promise((r) => {
54
- resolve = r;
55
- }));
56
- renderRoot();
57
- expect(screen.getByText(/loading/i)).toBeInTheDocument();
58
- resolve({ workspaces: [WS] });
59
- await waitFor(() => expect(screen.getByRole("heading", { name: "Tasks" })).toBeInTheDocument());
60
- });
61
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,37 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { describe, it, expect, vi } from "vitest";
3
- import { render, screen, waitFor } from "@testing-library/react";
4
- import { userEvent } from "@testing-library/user-event";
5
- import { ShepherdProvider } from "./ShepherdProvider.js";
6
- import { ShepherdRoot } from "./ShepherdRoot.js";
7
- import { makeMockClient } from "./test/mockClient.js";
8
- // Proves the jsdom + Testing Library harness works for downstream tasks: mount
9
- // the shell inside the provider and confirm the three-view tab skeleton renders
10
- // and switches. Task 6.3 made the shell async (it lands after listWorkspaces()
11
- // resolves), so we inject a mock client with a workspace and wait for the load.
12
- describe("ShepherdRoot", () => {
13
- const WS = { id: "ws_1", slug: "acme", name: "Acme", role: "admin" };
14
- function renderRoot() {
15
- const client = makeMockClient({
16
- listWorkspaces: vi.fn().mockResolvedValue({ workspaces: [WS] }),
17
- });
18
- return render(_jsx(ShepherdProvider, { client: client, children: _jsx(ShepherdRoot, {}) }));
19
- }
20
- it("mounts and shows the three-view tab shell", async () => {
21
- renderRoot();
22
- expect(await screen.findByRole("tab", { name: "Tasks" })).toBeInTheDocument();
23
- expect(screen.getByRole("tab", { name: "Chat" })).toBeInTheDocument();
24
- expect(screen.getByRole("tab", { name: "Config" })).toBeInTheDocument();
25
- });
26
- it("defaults to the Tasks view when a workspace exists", async () => {
27
- renderRoot();
28
- await waitFor(() => expect(screen.getByRole("tab", { name: "Tasks" })).toHaveAttribute("aria-selected", "true"));
29
- expect(screen.getByRole("heading", { name: "Tasks" })).toBeInTheDocument();
30
- });
31
- it("switches to the Config view when its tab is clicked", async () => {
32
- renderRoot();
33
- await userEvent.click(await screen.findByRole("tab", { name: "Config" }));
34
- expect(screen.getByRole("tab", { name: "Config" })).toHaveAttribute("aria-selected", "true");
35
- expect(screen.getByRole("heading", { name: "Config" })).toBeInTheDocument();
36
- });
37
- });