git ssb

16+

Dominic / patchbay



Commit 38af2fdb5719a156793f1114d29328b6f0386cd7

Merge branch 'master' into themes

Charles Lehner committed on 9/9/2016, 4:08:39 PM
Parent: 8298966ab96c8c27a2242f7a6f9f09269869b1c9
Parent: 7d994cda047fc045d76893bc013c17e2098c3b19

Files changed

README.mdchanged
modules/app.jschanged
modules/avatar-image.jschanged
modules/compose.jschanged
modules/feed.jschanged
modules/search-box.jschanged
modules/tabs.jschanged
modules/avatar_fallback.pngadded
modules/menu.jsadded
package.jsonchanged
style.csschanged
README.mdView
@@ -1,56 +1,19 @@
11 # patchbay
22
33 Prototype of a pluggable patchwork.
44
5-Patchbay uses [depject](https://npm.im/depject) to provide
6-a highly composable api. all scripts in the `./modules` directory
7-are loaded and combined using [depject](https://npm.im/depject)
5 +`patchbay` is an secure-scuttlebutt client interface
6 +that is fully compatible with [patchwork](https://github.com/ssbc/patchwork)
87
8 +I started `patchbay` to experiment with a different internal architecture
9 +based on [depject](https://github.com/dominictarr/depject). The goal was
10 +to make it easier to develop new features, and enable or disable features.
11 +This has so far been quite successful!
12 +
913 This makes in very easy to create say, a renderer for a new message type,
1014 or switch to a different method for choosing user names.
1115
12-## overview
13-
14-Currently, the main module is `tabs.js` which plugs into
15-the `app` socket, and provides the `screen_view` socket.
16-when you click a link, `screen_view` socket with the link path,
17-which returns a html element which becomes a new tab.
18-
19-currently, `main.js` `feed.js` and `thread.js` plug into `screen_view`
20-and then in turn, call the `message_render` socket, which calls
21-`message_content` `avatar` `message_meta` `message_action` and `message_link`.
22-
23-`avatar.js` plugs into `avatar`, and provides the `avatar_name` socket.
24-it just returns a link to the public key, labled with what it gets back
25-from `avatar_name` socket. this is in turn provided by the `names.js` module.
26-
27-Two modules plug into `message_content`, `post.js` and `like.js`
28-
29-No plugs into the `message_action` socket have been implemented yet,
30-but whatever is returned from this will inserted into the dom at the bottom
31-of the message (by the `message` module) so this would be the plug to
32-use for implementing a like/+1/fav/dig/yup button, or a reply button.
33-
34-## other ideas
35-
36-Editable messages would probably need to plug into several sockets.
37-firstly they would render content differently, so probably use the `message_content` socket.
38-secondly they would need to show edit state, which would probably use `message_meta`
39-and finially they'd need to provide the ability to edit the message!
40-that would use `message_action`
41-
42-Implementing a "events" message type would be easy, just implement another
43-plug for `message_content`, that renders events.
44-
45-Instead of reading all the modules in a directory, it would be better
46-to load these from configuration. Then, modules could be distributed
47-as browserify bundles, and distributed over ssb. Configuration
48-could just be a list of hashes - but you could also disable specific
49-sockets or plugs if necessary (leaving them unconnected).
50-
51-Then, that configuration could be shared over ssb!
52-
5316 ## Running
5417
5518 ```
5619 npm install scuttlebot@latest -g
@@ -75,10 +38,21 @@
7538 npm run build
7639 electro index.js
7740 ```
7841
79-## modules
42 +## how to add a feature
8043
44 +To add a new message type, add add a js to `./modules/` that
45 +exports a function named `message_content` (it should return an html element)
46 +To add a new tab, export a function named `screen_view` (returns an html element)
47 +
48 +To add a new detail, that appears above a message,
49 +export a function named `message_meta`.
50 +
51 +see the code for more examples.
52 +
53 +## module graph
54 +
8155 patchbay uses [depject](http://github.com/dominictarr/depject) to manage it's modules.
8256 here is a graph of the current connections between them. (round shows module,
8357 square shows api, arrow direction points from user to provider)
8458
@@ -86,4 +60,9 @@
8660
8761 ## License
8862
8963 MIT
64 +
65 +
66 +
67 +
68 +
modules/app.jsView
@@ -1,39 +1,19 @@
11 var plugs = require('../plugs')
22 var h = require('hyperscript')
33
44 var screen_view = plugs.first(exports.screen_view = [])
5-var menu_items = plugs.map(exports.menu_items = [])
65
7-var status = h('div.status.error') //start off disconnected
8-var list = h('div.column', {style: 'display: none;'})
96
10-var menu = h('div.menu.row', list, status, {
11- onmouseover: function (e) {
12- list.style.display = 'flex'
13- }, onmouseout: function () {
14- list.style.display = 'none'
15- }
16-})
17-
18-exports.connection_status = function (err) {
19- if(err) status.classList.add('error')
20- else status.classList.remove('error')
21-}
22-
237 exports.app = function () {
248 function hash() {
259 return window.location.hash.substring(1)
2610 }
2711
2812 var view = screen_view(hash() || 'tabs')
2913
30- var screen = h('div.screen.column', menu, view)
14 + var screen = h('div.screen.column', view)
3115
32- menu_items().forEach(function (el) {
33- list.appendChild(el)
34- })
35-
3616 window.onhashchange = function (ev) {
3717 var _view = view
3818 view = screen_view(hash() || 'tabs')
3919
@@ -69,4 +49,9 @@
6949
7050
7151
7252
53 +
54 +
55 +
56 +
57 +
modules/avatar-image.jsView
@@ -1,8 +1,9 @@
11
22 var getAvatar = require('ssb-avatar')
33 var h = require('hyperscript')
44 var ref = require('ssb-ref')
5 +var path = require('path')
56
67 var plugs = require('../plugs')
78 var sbot_query = plugs.first(exports.sbot_query = [])
89 var blob_url = require('../plugs').first(exports.blob_url = [])
@@ -10,9 +11,9 @@
1011 var pull = require('pull-stream')
1112
1213 var id = require('../keys').id
1314
14-var default_avatar = '&qjeAs8+uMXLlyovT4JnEpMwTNDx/QXHfOl2nv2u0VCM=.sha256'
15 +var default_avatar = path.join(__dirname, 'avatar_fallback.png')
1516
1617 var avatars = AVATARS = {}
1718
1819 function isFunction (f) {
@@ -77,9 +78,9 @@
7778 exports.avatar_image = function (author, classes) {
7879 classes = classes || ''
7980 if(classes && 'string' === typeof classes) classes = '.avatar--'+classes
8081
81- var img = h('img'+classes, {src: blob_url(default_avatar)})
82 + var img = h('img'+classes, {src: default_avatar})
8283 // getAvatar({links: sbot_links}, id, author, function (err, avatar) {
8384 // if (err) return console.error(err)
8485 // if(ref.isBlob(avatar.image))
8586 // img.src = blob_url(avatar.image)
modules/compose.jsView
@@ -22,9 +22,9 @@
2222 sbot = prepublish, prepublish = id
2323 var accessories
2424 meta = meta || {}
2525 if(!meta.type) throw new Error('message must have type')
26- var ta = h('textarea')
26 + var ta = h('textarea', {placeholder: 'Write a message'})
2727
2828 var blur
2929 ta.addEventListener('focus', function () {
3030 clearTimeout(blur)
modules/feed.jsView
@@ -8,8 +8,9 @@
88 var plugs = require('../plugs')
99 var sbot_user_feed = plugs.first(exports.sbot_user_feed = [])
1010 var message_render = plugs.first(exports.message_render = [])
1111 var avatar_profile = plugs.first(exports.avatar_profile = [])
12 +var signifier = plugs.first(exports.signifier = [])
1213
1314 exports.screen_view = function (id) {
1415 //TODO: header of user info, avatars, names, follows.
1516
@@ -23,8 +24,13 @@
2324 content
2425 )
2526 )
2627
28 + signifier(id, function (_, names) {
29 + if(names.length) div.title = names[0].name
30 + })
31 +
32 +
2733 pull(
2834 sbot_user_feed({id: id, old: false, live: true}),
2935 Scroller(div, content, message_render, true, false)
3036 )
@@ -47,6 +53,4 @@
4753
4854
4955
5056
51-
52-
modules/search-box.jsView
@@ -14,8 +14,9 @@
1414
1515 var suggestBox
1616 var search = h('input.searchprompt', {
1717 type: 'search',
18 + placeholder: '?word, @key, #channel',
1819 onkeydown: function (ev) {
1920 switch (ev.keyCode) {
2021 case 13: // enter
2122 if (suggestBox && suggestBox.active) {
modules/tabs.jsView
@@ -13,37 +13,52 @@
1313
1414 var plugs = require('../plugs')
1515 var screen_view = plugs.first(exports._screen_view = [])
1616 var search_box = plugs.first(exports.search_box = [])
17 +var menu = plugs.first(exports.menu = [])
1718
1819 exports.message_render = []
1920
2021 exports.screen_view = function (path) {
2122 if(path !== 'tabs')
2223 return
2324
25 + function setSelected (indexes) {
26 + var ids = indexes.map(function (index) {
27 + return tabs.get(index).id
28 + })
29 + if(ids.length > 1)
30 + search.value = 'split('+ids.join(',')+')'
31 + else
32 + search.value = ids[0]
33 + }
34 +
2435 var search
25- var tabs = Tabs(function (name) {
26- search.value = name
27-// sessionStorage.selectedTab = tabs.selected
28- })
29-// tabs.classList.add('screen')
36 + var tabs = Tabs(setSelected)
3037
3138 search = search_box(function (path, change) {
39 +
3240 if(tabs.has(path)) {
3341 tabs.select(path)
3442 return true
3543 }
3644 var el = screen_view(path)
45 +
3746 if(el) {
47 + if(!el.title) el.title = path
3848 el.scroll = keyscroll(el.querySelector('.scroller__content'))
39- tabs.add(path, el, change)
49 + tabs.add(el, change)
4050 // localStorage.openTabs = JSON.stringify(tabs.tabs)
4151 return change
4252 }
4353 })
4454
45- tabs.insertBefore(search, tabs.firstChild.nextSibling)
55 + //reposition hypertabs menu to inside a container...
56 + tabs.insertBefore(h('div.header.row',
57 + h('div.header__tabs.row', tabs.firstChild), //tabs
58 + h('div.header__search.row.end', h('div', search), menu())
59 + ), tabs.firstChild)
60 +// tabs.insertBefore(search, tabs.firstChild.nextSibling)
4661
4762 var saved = []
4863 // try { saved = JSON.parse(localStorage.openTabs) }
4964 // catch (_) { }
@@ -52,16 +67,17 @@
5267 saved = ['/public', '/private', '/notifications']
5368
5469 saved.forEach(function (path) {
5570 var el = screen_view(path)
71 + el.id = el.id || path
5672 if (!el) return
5773 el.scroll = keyscroll(el.querySelector('.scroller__content'))
58- if(el) tabs.add(path, el, false)
74 + if(el) tabs.add(el, false, false)
5975 })
6076
61-// tabs.select(sessionStorage.selectedTab || saved[0] || '/public')
62- tabs.select('/public')
77 + tabs.select(0)
6378
79 + //handle link clicks
6480 window.onclick = function (ev) {
6581 var link = ancestor(ev.target)
6682 if(!link) return
6783 var path = link.hash.substring(1)
@@ -72,14 +88,16 @@
7288 //open external links.
7389 //this ought to be made into something more runcible
7490 if(open.isExternal(link.href)) return open(link.href)
7591
76- if(tabs.has(path)) return tabs.select(path)
77-
92 + if(tabs.has(path))
93 + return tabs.select(path, !ev.ctrlKey, !!ev.shiftKey)
94 +
7895 var el = screen_view(path)
7996 if(el) {
97 + el.id = el.id || path
8098 el.scroll = keyscroll(el.querySelector('.scroller__content'))
81- tabs.add(path, el, !ev.ctrlKey)
99 + tabs.add(el, !ev.ctrlKey, !!ev.shiftKey)
82100 // localStorage.openTabs = JSON.stringify(tabs.tabs)
83101 }
84102
85103 return false
@@ -97,19 +115,19 @@
97115 return tabs.selectRelative(1)
98116
99117 // scroll through messages
100118 case 74: // j
101- return tabs.selectedTab.scroll(1)
119 + return tabs.get(tabs.selected[0]).scroll(1)
102120 case 75: // k
103- return tabs.selectedTab.scroll(-1)
121 + return tabs.get(tabs.selected[0]).scroll(-1)
104122
105123 // close a tab
106124 case 88: // x
107- if (tabs.selected/* && tabs.selected[0] !== '/'*/) {
125 + if (tabs.selected) {
108126 var sel = tabs.selected
109- tabs.selectRelative(-1)
127 + var i = sel.reduce(function (a, b) { return Math.min(a, b) })
110128 tabs.remove(sel)
111-// localStorage.openTabs = JSON.stringify(tabs.tabs)
129 + tabs.select(Math.max(i-1, 0))
112130 }
113131 return
114132
115133 // activate the search field
@@ -154,4 +172,13 @@
154172 })
155173
156174 return tabs
157175 }
176 +
177 +
178 +
179 +
180 +
181 +
182 +
183 +
184 +
modules/avatar_fallback.png
modules/avatar_fallback.png
modules/menu.jsView
@@ -1,0 +1,31 @@
1 +var plugs = require('../plugs')
2 +var h = require('hyperscript')
3 +
4 +var menu_items = plugs.map(exports.menu_items = [])
5 +
6 +var status = h('div.status.error') //start off disconnected
7 + var list = h('div.column', {style: 'display: none;'})
8 +
9 +var menu = h('div.menu.column', status, list , {
10 + onmouseover: function (e) {
11 + list.style.display = 'flex'
12 + }, onmouseout: function () {
13 + list.style.display = 'none'
14 + }
15 +})
16 +
17 +exports.connection_status = function (err) {
18 + if(err) status.classList.add('error')
19 + else status.classList.remove('error')
20 +}
21 +
22 +exports.menu = function () {
23 + menu_items().forEach(function (el) {
24 + list.appendChild(el)
25 + })
26 +
27 + return menu
28 +}
29 +
30 +
31 +
package.jsonView
@@ -1,8 +1,8 @@
11 {
22 "name": "patchbay",
33 "description": "a pluggable patchwork",
4- "version": "2.6.0",
4 + "version": "3.0.1",
55 "homepage": "https://github.com/dominictarr/patchbay",
66 "repository": {
77 "type": "git",
88 "url": "git://github.com/dominictarr/patchbay.git"
@@ -18,9 +18,9 @@
1818 "hyperfile": "^1.1.0",
1919 "hyperlightbox": "^0.1.3",
2020 "hyperprogress": "0.1.0",
2121 "hyperscript": "^1.4.7",
22- "hypertabs": "^1.2.0",
22 + "hypertabs": "^2.2.0",
2323 "is-visible": "^2.0.4",
2424 "map-filter-reduce": "^3.0.1",
2525 "mime-types": "^2.1.11",
2626 "moment": "^2.13.0",
style.cssView
@@ -1,8 +1,12 @@
11 body {
22 font-family: sans-serif;
33 }
44
5 +* {
6 + word-break: break-word;
7 +}
8 +
59 .screen {
610 position: absolute;
711 top: 0px; bottom: 0px;
812 left: 0px; right: 0px;
@@ -11,28 +15,41 @@
1115
1216 .column {
1317 display: flex;
1418 flex-direction: column;
15- min-height: 0px;
1619 }
1720
1821 .row {
1922 display: flex;
2023 flex-direction: row;
2124 }
2225
26 +.end {
27 + justify-content: flex-end;
28 +}
29 +
2330 .wrap {
2431 display: flex;
2532 flex-direction: row;
2633 flex-wrap: wrap;
2734 }
2835
36 +.no-shrink {
37 + flex-shrink: 0;
38 +}
39 +
40 +.expand {
41 + justify-content: space-between;
42 +}
43 +
2944 .scroll-y {
3045 overflow-y: auto;
46 + min-height: 0px;
3147 }
3248
3349 .scroll-x {
3450 overflow-x: auto;
51 + min-width: 0px;
3552 }
3653
3754 pre {
3855 white-space: pre-wrap;
@@ -67,34 +84,8 @@
6784 margin-left: auto;
6885 margin-right: auto;
6986 }
7087
71-/* --- hypertabs ------- */
72-
73-.hypertabs__tabs {
74- overflow-y: hide;
75-}
76-
77-.hypertabs > .row {
78- flex-grow: 0;
79- flex-shrink: 0;
80- margin: 0;
81-}
82-
83-.hypertabs__tabs > * {
84- max-width: 4em;
85- overflow-x: hidden;
86- margin-right: .5ex;
87- padding-top: .1ex;
88-}
89-
90-.hypertabs--selected {
91- max-width: 4em;
92- background: yellow;
93- padding-left: .5ex;
94- padding-right: .5ex;
95-}
96-
9788 /* compose */
9889
9990 .compose {
10091 width: 100%;
@@ -156,15 +147,11 @@
156147
157148 /* -- suggest box */
158149
159150 .suggest-box > * {
160- margin: .5ex;
151 + display: block;
161152 }
162153
163-.suggest-box {
164- width: 5em;
165-}
166-
167154 .suggest-box ul {
168155 list-style-type: none;
169156 padding-left: -2em;
170157 }
@@ -172,8 +159,13 @@
172159 .suggest-box .selected {
173160 background: yellow;
174161 }
175162
163 +.suggest-box {
164 + width: max-content;
165 + background: white;
166 +}
167 +
176168 /* avatar */
177169
178170 .avatar {
179171 display: flex;
@@ -218,37 +210,18 @@
218210
219211 /* searchprompt */
220212
221213 .searchprompt {
222- width: 17em;
223- margin-left: .5ex;
224- margin-bottom: .5ex;
214 + width: 100%;
215 + height: 2.55em;
225216 }
226217
227218 /* TextNodeSearcher highlights */
228219
229220 .highlight {
230221 background: yellow;
231222 }
232223
233-/* --- network status --- */
234-
235-.status {
236- width: 20px;
237- height: 20px;
238- background: green;
239-}
240-
241-.menu {
242- position: fixed;
243- right: 10px;
244- top: 10px;
245-}
246-
247-.error {
248- background: red;
249-}
250-
251224 /* avatar editor */
252225
253226 .hypercrop__canvas {
254227 width: 100%;
@@ -271,11 +244,9 @@
271244
272245 .status {
273246 width: .7em;
274247 height: .7em;
275- position: fixed;
276- right: .8em;
277- top: .8em;
248 + margin: .7em;
278249 border-radius: 100%;
279250 background: green;
280251 }
281252
@@ -300,6 +271,69 @@
300271 margin: 1ex;
301272 }
302273
303274
275 +/* tabs */
304276
277 +.header {
278 + background: lightgray;
279 + border-bottom: 2px inset;
280 + flex-shrink: 0;
281 +}
305282
283 +.header__tabs {
284 + width: 100%;
285 + min-width: 0px;
286 +}
287 +
288 +/* --- hypertabs ------- */
289 +
290 +.hypertabs__tabs {
291 + min-width: 0px;
292 + width: 100%;
293 +}
294 +
295 +.hypertabs__tab {
296 + overflow-x: hidden;
297 + min-width: 0px;
298 + width: 100%;
299 +}
300 +
301 +.hypertabs__button {
302 + overflow-x: hidden;
303 + min-width: 0px;
304 + width: 100%;
305 +}
306 +
307 +.hypertabs__tab {
308 + color: black;
309 + background: lightgray;
310 + border-right: 1px solid #ccc;
311 + border-top-left-radius: 5px;
312 + margin-left: -3px;
313 + border-bottom: none;
314 + padding-top: .35em;
315 + padding-left: .5em;
316 + width: 100%;
317 +}
318 +
319 +.hypertabs__tab > a {
320 + color: black;
321 + text-decoration: none;
322 + white-space: nowrap;
323 +}
324 +
325 +.hypertabs--selected {
326 + background: #E7E7E7;
327 + border-top-right-radius: 3px;
328 + z-index: 1;
329 +}
330 +
331 +.hypertabs__x {
332 + display: none;
333 + transform: translate(-5px, -3px);
334 +}
335 +
336 +.hypertabs--selected .hypertabs__x {
337 + display: block;
338 +}
339 +

Built with git-ssb-web