Commit 896d9cb98b2e5f5c21d03292502be08b22a16f1a
Merge branch 'master' into hashrouter
Dominic Tarr committed on 7/25/2016, 11:32:13 PMParent: 635ea39199988640e918cccefadd0bac2abfdf36
Parent: 2b5c84eb99803edd36a6454033c6011d16b42479
Files changed
.gitignore | changed |
README.md | changed |
index.js | changed |
modules/about.js | changed |
modules/avatar-image.js | changed |
modules/avatar-profile.js | changed |
modules/avatar.js | changed |
modules/follow.js | changed |
modules/index.js | changed |
modules/invite.js | changed |
modules/message-confirm.js | changed |
modules/message.js | changed |
modules/suggest-mentions.js | changed |
modules/audio-mp3.js | added |
modules/avatar-edit.js | added |
modules/git.js | added |
modules/meta-image.js | added |
modules/music-release-cc.js | added |
modules/music-release.js | added |
modules/relationships.js | added |
package.json | changed |
sbot-api.js | changed |
style.css | changed |
README.md | ||
---|---|---|
@@ -69,8 +69,9 @@ | ||
69 | 69 | now clone and run patchboard. |
70 | 70 | ``` |
71 | 71 | git clone https://github.com/dominictarr/patchbay.git |
72 | 72 | cd patchbay |
73 | +npm install | |
73 | 74 | npm install electro electron-prebuilt -g |
74 | 75 | electro index.js |
75 | 76 | ``` |
76 | 77 |
index.js | ||
---|---|---|
@@ -1,5 +1,13 @@ | ||
1 | 1 | var h = require('hyperscript') |
2 | + | |
3 | +window.addEventListener('error', function onError(e) { | |
4 | + document.body.appendChild(h('div.error', | |
5 | + h('h1', e.message), | |
6 | + h('big', h('code', e.filename + ':' + e.lineno)), | |
7 | + h('pre', e.error ? (e.error.stack || e.error.toString()) : e.toString()))) | |
8 | +}) | |
9 | + | |
2 | 10 | var u = require('./util') |
3 | 11 | var pull = require('pull-stream') |
4 | 12 | var combine = require('depject') |
5 | 13 | var fs = require('fs') |
modules/about.js | ||
---|---|---|
@@ -26,9 +26,9 @@ | ||
26 | 26 | ' as ', |
27 | 27 | h('a', {href:"#"+about.about}, |
28 | 28 | about.name || null, |
29 | 29 | about.image |
30 | - ? h('img', {src: blob_url(about.image)}) | |
30 | + ? h('img.avatar--fullsize', {src: blob_url(about.image)}) | |
31 | 31 | : null |
32 | 32 | ) |
33 | 33 | ) |
34 | 34 |
modules/avatar-image.js | ||
---|---|---|
@@ -10,10 +10,13 @@ | ||
10 | 10 | var id = require('../keys').id |
11 | 11 | |
12 | 12 | var default_avatar = '&qjeAs8+uMXLlyovT4JnEpMwTNDx/QXHfOl2nv2u0VCM=.sha256' |
13 | 13 | |
14 | -exports.avatar_image = function (author) { | |
15 | - var img = h('img', {src: blob_url(default_avatar)}) | |
14 | +exports.avatar_image = function (author, classes) { | |
15 | + classes = classes || '' | |
16 | + if(classes && 'string' === typeof classes) classes = '.avatar--'+classes | |
17 | + | |
18 | + var img = h('img'+classes, {src: blob_url(default_avatar)}) | |
16 | 19 | getAvatar({links: sbot_links}, id, author, function (err, avatar) { |
17 | 20 | if (err) return console.error(err) |
18 | 21 | if(ref.isBlob(avatar.image)) |
19 | 22 | img.src = blob_url(avatar.image) |
modules/avatar-profile.js | ||
---|---|---|
@@ -1,13 +1,50 @@ | ||
1 | 1 | var h = require('hyperscript') |
2 | 2 | var plugs = require('../plugs') |
3 | +var pull = require('pull-stream') | |
3 | 4 | |
4 | 5 | var avatar_image = plugs.first(exports.avatar_image = []) |
6 | +var avatar_name = plugs.first(exports.avatar_name = []) | |
5 | 7 | var avatar_action = plugs.map(exports.avatar_action = []) |
8 | +var avatar_edit = plugs.first(exports.avatar_edit = []) | |
6 | 9 | |
10 | +var follows = plugs.first(exports.follows = []) | |
11 | +var followers = plugs.first(exports.followers = []) | |
12 | + | |
13 | +function streamToList(stream, el) { | |
14 | + pull( | |
15 | + stream, | |
16 | + pull.drain(function (item) { | |
17 | + if(item) el.appendChild(item) | |
18 | + }) | |
19 | + ) | |
20 | + return el | |
21 | +} | |
22 | + | |
23 | +function image_link (id) { | |
24 | + return h('a', {href:'#'+id}, avatar_image(id, 'thumbnail')) | |
25 | +} | |
26 | + | |
7 | 27 | exports.avatar_profile = function (id) { |
28 | + return h('div.column.profile', | |
29 | + avatar_edit(id), | |
30 | + avatar_action(id), | |
8 | 31 | |
9 | - return h('div.row.profile', | |
10 | - avatar_image(id), | |
11 | - h('div.column', avatar_action(id)) | |
32 | + h('div.profile__relationships.column', | |
33 | + | |
34 | + h('strong', 'follows'), | |
35 | + streamToList(pull( | |
36 | + follows(id), | |
37 | + pull.unique(), | |
38 | + pull.map(image_link) | |
39 | + ), h('div.profile__follows.wrap')), | |
40 | + | |
41 | + h('strong', 'followers'), | |
42 | + streamToList(pull( | |
43 | + followers(id), | |
44 | + pull.unique(), | |
45 | + pull.map(image_link) | |
46 | + ), h('div.profile__followers.wrap')) | |
47 | + ) | |
12 | 48 | ) |
13 | 49 | } |
50 | + |
modules/avatar.js | ||
---|---|---|
@@ -5,12 +5,12 @@ | ||
5 | 5 | var plugs = require('../plugs') |
6 | 6 | var avatar_name = plugs.first(exports.avatar_name = []) |
7 | 7 | var avatar_image = plugs.first(exports.avatar_image = []) |
8 | 8 | |
9 | -exports.avatar = function (author) { | |
9 | +exports.avatar = function (author, classes) { | |
10 | 10 | return h('a.avatar', |
11 | 11 | {href:'#'+author}, |
12 | - avatar_image(author), | |
12 | + avatar_image(author, classes), | |
13 | 13 | avatar_name(author) |
14 | 14 | ) |
15 | 15 | } |
16 | 16 |
modules/follow.js | ||
---|---|---|
@@ -5,15 +5,19 @@ | ||
5 | 5 | var plugs = require('../plugs') |
6 | 6 | |
7 | 7 | //render a message when someone follows someone, |
8 | 8 | //so you see new users |
9 | +function isRelated(value, name) { | |
10 | + return value ? name : value === false ? 'un'+name : '' | |
11 | +} | |
12 | + | |
9 | 13 | exports.message_content = function (msg) { |
10 | 14 | |
11 | - if(msg.value.content.type == 'contact' && msg.value.content.contact) { | |
12 | - return h('div.contact', | |
13 | - 'follows', | |
14 | - avatar(msg.value.content.contact) | |
15 | - ) | |
15 | + var content = msg.value.content | |
16 | + if(content.type == 'contact' && content.contact) { | |
17 | + var relation = isRelated(content.following, 'follows') | |
18 | + if(content.blocking) relation = 'blocks' | |
19 | + return h('div.contact', relation, avatar(msg.value.content.contact, 'thumbnail')) | |
16 | 20 | } |
17 | 21 | } |
18 | 22 | |
19 | 23 | var sbot_links2 = plugs.first(exports.sbot_links2 = []) |
@@ -81,4 +85,26 @@ | ||
81 | 85 | }}, h('br'), label) |
82 | 86 | ) |
83 | 87 | } |
84 | 88 | |
89 | + | |
90 | + | |
91 | + | |
92 | + | |
93 | + | |
94 | + | |
95 | + | |
96 | + | |
97 | + | |
98 | + | |
99 | + | |
100 | + | |
101 | + | |
102 | + | |
103 | + | |
104 | + | |
105 | + | |
106 | + | |
107 | + | |
108 | + | |
109 | + | |
110 | + |
modules/index.js | ||
---|---|---|
@@ -1,8 +1,10 @@ | ||
1 | 1 | module.exports = { |
2 | 2 | "_screen_view.js": require('./_screen_view.js'), |
3 | 3 | "about.js": require('./about.js'), |
4 | 4 | "app.js": require('./app.js'), |
5 | + "avatar-edit.js": require('./avatar-edit.js'), | |
6 | + "audio-mp3.js": require('./audio-mp3.js'), | |
5 | 7 | "avatar-image.js": require('./avatar-image.js'), |
6 | 8 | "avatar-profile.js": require('./avatar-profile.js'), |
7 | 9 | "avatar.js": require('./avatar.js'), |
8 | 10 | "blob-url.js": require('./blob-url.js'), |
@@ -11,15 +13,20 @@ | ||
11 | 13 | "crypto.js": require('./crypto.js'), |
12 | 14 | "feed.js": require('./feed.js'), |
13 | 15 | "file-input.js": require('./file-input.js'), |
14 | 16 | "follow.js": require('./follow.js'), |
17 | + "relationships.js": require('./relationships.js'), | |
18 | + "git.js": require('./git.js'), | |
15 | 19 | "invite.js": require('./invite.js'), |
16 | 20 | "like.js": require('./like.js'), |
17 | 21 | "markdown.js": require('./markdown.js'), |
18 | 22 | "message-confirm.js": require('./message-confirm.js'), |
19 | 23 | "message-link.js": require('./message-link.js'), |
20 | 24 | "message-name.js": require('./message-name.js'), |
21 | 25 | "message.js": require('./message.js'), |
26 | + "meta-image.js": require('./meta-image.js'), | |
27 | + "music-release-cc.js": require('./music-release-cc.js'), | |
28 | + "music-release.js": require('./music-release.js'), | |
22 | 29 | "names.js": require('./names.js'), |
23 | 30 | "notifications.js": require('./notifications.js'), |
24 | 31 | "post.js": require('./post.js'), |
25 | 32 | "private.js": require('./private.js'), |
@@ -32,4 +39,13 @@ | ||
32 | 39 | "tabs.js": require('./tabs.js'), |
33 | 40 | "thread.js": require('./thread.js'), |
34 | 41 | "timestamp.js": require('./timestamp.js') |
35 | 42 | } |
43 | + | |
44 | + | |
45 | + | |
46 | + | |
47 | + | |
48 | + | |
49 | + | |
50 | + | |
51 | + |
modules/invite.js | ||
---|---|---|
@@ -3,91 +3,83 @@ | ||
3 | 3 | var ssbClient = require('ssb-client') |
4 | 4 | var id = require('../keys').id |
5 | 5 | var h = require('hyperscript') |
6 | 6 | |
7 | +var Progress = require('hyperprogress') | |
8 | + | |
7 | 9 | var plugs = require('../plugs') |
8 | 10 | var sbot_publish = plugs.first(exports.sbot_publish = []) |
9 | 11 | |
10 | 12 | |
11 | -exports.screen_view = function (invite) { | |
12 | - | |
13 | - //check that invite is | |
14 | - // ws:...~shs:key:seed | |
15 | - | |
13 | +//check that invite is | |
14 | +// ws:...~shs:key:seed | |
15 | +function parseMultiServerInvite (invite) { | |
16 | 16 | var parts = invite.split('~') |
17 | 17 | .map(function (e) { return e.split(':') }) |
18 | 18 | |
19 | 19 | if(parts.length !== 2) return null |
20 | 20 | if(!/^(net|wss?)$/.test(parts[0][0])) return null |
21 | 21 | if(parts[1][0] !== 'shs') return null |
22 | 22 | if(parts[1].length !== 3) return null |
23 | + var p2 = invite.split(':') | |
24 | + p2.pop() | |
23 | 25 | |
26 | + return { | |
27 | + invite: invite, | |
28 | + remote: p2.join(':'), | |
29 | + } | |
30 | +} | |
31 | + | |
32 | +exports.screen_view = function (invite) { | |
33 | + | |
34 | + var data = parseMultiServerInvite(invite) | |
35 | + if(!data) return | |
36 | + | |
37 | + var progress = Progress(4) | |
38 | + | |
24 | 39 | //connect to server |
25 | 40 | //request follow |
26 | 41 | //post pub announce |
27 | 42 | //post follow pub |
28 | - var progress = h('h1') | |
29 | - var status = h('pre') | |
30 | - var div = h('div', | |
31 | - progress, status, | |
32 | - h('a', 'accept', {href: '#', onclick: function (ev) { | |
33 | - ev.preventDefault() | |
34 | - ev.stopPropagation() | |
35 | - attempt() | |
36 | - return false | |
37 | - }}) | |
43 | + var div = h('div.column', | |
44 | + h('div', | |
45 | + "invite to:", h('br'), | |
46 | + h('code', invite), | |
47 | + h('button', 'accept', {onclick: function (ev) { | |
48 | + attempt() | |
49 | + }}) | |
50 | + ), | |
51 | + progress | |
38 | 52 | ) |
39 | 53 | |
40 | 54 | function attempt () { |
41 | - progress.textContent = '*' | |
42 | - status.textContent = 'connecting...' | |
55 | + progress.reset().next('connecting...') | |
43 | 56 | |
44 | - console.log("CONNECT", invite) | |
45 | 57 | ssbClient(null, { |
46 | 58 | remote: invite, |
47 | 59 | manifest: { invite: {use: 'async'}, getAddress: 'async' } |
48 | 60 | }, function (err, sbot) { |
49 | - console.log("ERR?", err, sbot) | |
50 | - if(err) { | |
51 | - progress.textContent = '*!' | |
52 | - status.textContent = err.stack | |
53 | - return | |
54 | - } | |
55 | - progress.textContent = '**' | |
56 | - status.textContent = 'requesting follow...' + id | |
61 | + if(err) return progress.fail(err) | |
62 | + progress.next('requesting follow...') | |
57 | 63 | |
58 | 64 | sbot.invite.use({feed: id}, function (err, msg) { |
59 | - if(err) { | |
60 | - progress.textContent = '**!' | |
61 | - status.textContent = err.stack | |
62 | - return | |
63 | - } | |
64 | - progress.textContent = '***' | |
65 | - status.textContent = 'following...' | |
66 | - | |
65 | + if(err) return progress.fail(err) | |
66 | + progress.next('following...') | |
67 | + | |
67 | 68 | //remove the seed from the shs address. |
68 | 69 | //then it's correct address. |
69 | 70 | //this should make the browser connect to this as remote. |
70 | 71 | //we don't want to do this if when using this locally, though. |
71 | - if(process.title === 'browser') { | |
72 | - var p2 = invite.split(':') | |
73 | - p2.pop() | |
74 | - localStorage.remote = p2.join(':') | |
75 | - } | |
72 | + if(process.title === 'browser') | |
73 | + localStorage.remote = data.remote | |
76 | 74 | |
77 | 75 | sbot_publish({ |
78 | 76 | type: 'contact', |
79 | 77 | contact: sbot.id, |
80 | - following: true | |
78 | + following: true, | |
81 | 79 | }, function (err) { |
82 | - if(err) { | |
83 | - progress.textContent = '***!' | |
84 | - status.textContent = err.stack | |
85 | - return | |
86 | - } | |
87 | - progress.textContent = '****' | |
88 | - status.textContent = 'READY!' | |
89 | - | |
80 | + if(err) return progress.fail(err) | |
81 | + progress.complete() | |
90 | 82 | }) |
91 | 83 | |
92 | 84 | }) |
93 | 85 | }) |
@@ -95,5 +87,4 @@ | ||
95 | 87 | |
96 | 88 | return div |
97 | 89 | } |
98 | 90 | |
99 | - |
modules/message-confirm.js | ||
---|---|---|
@@ -15,9 +15,10 @@ | ||
15 | 15 | var lb = lightbox() |
16 | 16 | document.body.appendChild(lb) |
17 | 17 | |
18 | 18 | var okay = h('button', 'okay', {onclick: function () { |
19 | - publish(content); lb.remove(); cb(null, content) | |
19 | + lb.remove() | |
20 | + publish(content, cb) | |
20 | 21 | }}) |
21 | 22 | |
22 | 23 | var cancel = h('button', 'cancel', {onclick: function () { |
23 | 24 | lb.remove() |
modules/message.js | ||
---|---|---|
@@ -32,9 +32,9 @@ | ||
32 | 32 | ) |
33 | 33 | |
34 | 34 | var msg = h('div.message', |
35 | 35 | h('div.title.row', |
36 | - h('div.avatar', avatar(msg.value.author)), | |
36 | + h('div.avatar', avatar(msg.value.author, 'thumbnail')), | |
37 | 37 | h('div.message_meta.row', message_meta(msg)) |
38 | 38 | ), |
39 | 39 | h('div.message_content', el), |
40 | 40 | h('div.message_actions.row', |
modules/suggest-mentions.js | ||
---|---|---|
@@ -4,8 +4,9 @@ | ||
4 | 4 | return /\.(gif|jpg|png|svg)$/i.test(filename) |
5 | 5 | } |
6 | 6 | |
7 | 7 | var sbot_links2 = require('../plugs').first(exports.sbot_links2 = []) |
8 | +var blob_url = require('../plugs').first(exports.blob_url = []) | |
8 | 9 | |
9 | 10 | exports.suggest = cont.to(function (word, cb) { |
10 | 11 | if(!/^[@%&!]/.test(word[0])) return cb() |
11 | 12 | if(word.length < 2) return cb() |
@@ -32,12 +33,13 @@ | ||
32 | 33 | return { |
33 | 34 | title: e.name + ': ' + e.id.substring(0,10)+' ('+e.rank+')', |
34 | 35 | value: embed+'['+e.name+']('+e.id+')', |
35 | 36 | rank: e.rank, |
36 | - image: isImage(e.name) ? 'http://localhost:7777/'+e.id : undefined | |
37 | + image: isImage(e.name) ? blob_url(e.id) : undefined | |
37 | 38 | } |
38 | 39 | }) |
39 | 40 | cb(null, ary) |
40 | 41 | }) |
41 | 42 | ) |
42 | 43 | }) |
43 | 44 | |
45 | + |
modules/audio-mp3.js | ||
---|---|---|
@@ -1,0 +1,51 @@ | ||
1 | +var markdown = require('ssb-markdown'); | |
2 | +var h = require('hyperscript'); | |
3 | +var u = require('../util'); | |
4 | +var ref = require('ssb-ref'); | |
5 | + | |
6 | +//render a message | |
7 | + | |
8 | +var plugs = require('../plugs'); | |
9 | +var message_link = plugs.first(exports.message_link = []); | |
10 | +var message_confirm = plugs.first(exports.message_confirm = []); | |
11 | +var sbot_links = plugs.first(exports.sbot_links = []); | |
12 | +var blob_url = plugs.first(exports.blob_url = []); | |
13 | + | |
14 | +exports.message_content = function(msg, sbot) { | |
15 | + if (msg.value.content.type !== 'audio-mp3') | |
16 | + return; | |
17 | + | |
18 | + var v = msg.value.content; | |
19 | + return h('div', | |
20 | + h('h2', "(" + v.Track + ") " + v.Title), | |
21 | + // h('img', { "src" : blob_url(v.cover) }), | |
22 | + h('audio', { | |
23 | + "controls" : true, | |
24 | + "src" : blob_url(v.link) | |
25 | + })) | |
26 | + // h('dl', | |
27 | + // Object.keys(v).map(function(k) { | |
28 | + // return [ | |
29 | + // h("dt", k), | |
30 | + // h("dd", v[k]), | |
31 | + // ] | |
32 | + // }))) | |
33 | + | |
34 | + // "Album": "the fall of", | |
35 | + // "Crc32": "038becab", | |
36 | + // "Creator": "bleupulp", | |
37 | + // "Format": "VBR MP3", | |
38 | + // "Height": "0", | |
39 | + // "Length": "375.23", | |
40 | + // "Md5": "2c517c8e813da5f940c8c7e77d4b7f3f", | |
41 | + // "Mtime": "1399498698", | |
42 | + // "Name": "2_bleupulp_-_clouds.mp3", | |
43 | + // "Sha1": "9f6a96a3d5571ed1ec2a7da38ffebdcd5f181482", | |
44 | + // "Size": "15009000", | |
45 | + | |
46 | + // "Title": "clouds", | |
47 | + // "Track": "2", | |
48 | + // "Width": "0", | |
49 | + | |
50 | +} | |
51 | + |
modules/avatar-edit.js | ||
---|---|---|
@@ -1,0 +1,147 @@ | ||
1 | +var dataurl = require('dataurl') | |
2 | +var hyperfile = require('hyperfile') | |
3 | +var hypercrop = require('hypercrop') | |
4 | +var hyperlightbox = require('hyperlightbox') | |
5 | +var h = require('hyperscript') | |
6 | +var pull = require('pull-stream') | |
7 | +var getAvatar = require('ssb-avatar') | |
8 | +var plugs = require('../plugs') | |
9 | +var ref = require('ssb-ref') | |
10 | + | |
11 | +var self_id = require('../keys').id | |
12 | +var default_avatar = '&qjeAs8+uMXLlyovT4JnEpMwTNDx/QXHfOl2nv2u0VCM=.sha256' | |
13 | + | |
14 | +var confirm = plugs.first(exports.message_confirm = []) | |
15 | +var sbot_blobs_add = plugs.first(exports.sbot_blobs_add = []) | |
16 | +var blob_url = plugs.first(exports.blob_url = []) | |
17 | +var sbot_links = plugs.first(exports.sbot_links = []) | |
18 | +var avatar_name = plugs.first(exports.avatar_name = []) | |
19 | + | |
20 | +function crop (d, cb) { | |
21 | + var data | |
22 | + var canvas = hypercrop(h('img', {src: d})) | |
23 | + | |
24 | + return h('div.column.avatar_pic', | |
25 | + canvas, | |
26 | + //canvas.selection, | |
27 | + h('div.row.avatar_pic__controls', | |
28 | + h('button', 'okay', {onclick: function () { | |
29 | + cb(null, canvas.selection.toDataURL()) | |
30 | + }}), | |
31 | + h('button', 'cancel', {onclick: function () { | |
32 | + cb(new Error('canceled')) | |
33 | + }}) | |
34 | + ) | |
35 | + ) | |
36 | +} | |
37 | + | |
38 | +exports.avatar_edit = function (id) { | |
39 | + | |
40 | + var img = h('img.avatar--large', {src: blob_url(default_avatar)}) | |
41 | + var lb = hyperlightbox() | |
42 | + var name_input = h('input', {placeholder: 'rename'}) | |
43 | + var name = avatar_name(id) | |
44 | + var selected = null, selected_data = null | |
45 | + | |
46 | + getAvatar({links: sbot_links}, self_id, id, function (err, avatar) { | |
47 | + if (err) return console.error(err) | |
48 | + //don't show user has already selected an avatar. | |
49 | + if(selected) return | |
50 | + if(ref.isBlob(avatar.image)) | |
51 | + img.src = blob_url(avatar.image) | |
52 | + }) | |
53 | + | |
54 | + var also_pictured = h('div.profile__alsopicturedas.wrap') | |
55 | + | |
56 | + pull( | |
57 | + sbot_links({dest: id, rel: 'about', values: true}), | |
58 | + pull.map(function (e) { | |
59 | + return e.value.content.image | |
60 | + }), | |
61 | + pull.filter(function (e) { | |
62 | + return e && 'string' == typeof e.link | |
63 | + }), | |
64 | + pull.unique('link'), | |
65 | + pull.drain(function (image) { | |
66 | + also_pictured.appendChild( | |
67 | + h('a', {href:'#', onclick: function (ev) { | |
68 | + ev.stopPropagation() | |
69 | + ev.preventDefault() | |
70 | + selected = image | |
71 | + img.src = blob_url(image.link || image) | |
72 | + }}, | |
73 | + h('img.avatar--thumbnail', {src: blob_url(image)}) | |
74 | + ) | |
75 | + ) | |
76 | + }) | |
77 | + ) | |
78 | + | |
79 | + return h('div.row.profile', | |
80 | + lb, | |
81 | + img, | |
82 | + h('div.column.profile__info', | |
83 | + h('strong', name), | |
84 | + name_input, | |
85 | + | |
86 | + hyperfile.asDataURL(function (data) { | |
87 | + var el = crop(data, function (err, data) { | |
88 | + if(data) { | |
89 | + img.src = data | |
90 | + pull( | |
91 | + pull.once(dataurl.parse(data)), | |
92 | + sbot_blobs_add(function (err, hash) { | |
93 | + //TODO. Alerts are EVIL. | |
94 | + //I use them only in a moment of weakness. | |
95 | + | |
96 | + if(err) return alert(err.stack) | |
97 | + selected = { | |
98 | + link: hash, | |
99 | + size: selected.data.length, | |
100 | + type: selected.mimetype, | |
101 | + width: 512, | |
102 | + height: 512 | |
103 | + } | |
104 | + | |
105 | + }) | |
106 | + ) | |
107 | + } | |
108 | + lb.close() | |
109 | + }) | |
110 | + lb.show(el) | |
111 | + }), | |
112 | + h('button', 'update', {onclick: function () { | |
113 | + if(name_input.value) | |
114 | + name.textContent = name_input.value | |
115 | + | |
116 | + if(selected) | |
117 | + confirm({ | |
118 | + type: 'about', | |
119 | + about: id, | |
120 | + name: name_input.value || undefined, | |
121 | + image: selected | |
122 | + }) | |
123 | + else if(name_input.value) //name only | |
124 | + confirm({ | |
125 | + type: 'about', | |
126 | + about: id, | |
127 | + name: name_input.value || undefined, | |
128 | + }) | |
129 | + else | |
130 | + //another moment of weakness | |
131 | + alert('must select a name or image') | |
132 | + }}), | |
133 | + also_pictured | |
134 | + ) | |
135 | + ) | |
136 | +} | |
137 | + | |
138 | + | |
139 | + | |
140 | + | |
141 | + | |
142 | + | |
143 | + | |
144 | + | |
145 | + | |
146 | + | |
147 | + |
modules/git.js | ||
---|---|---|
@@ -1,0 +1,250 @@ | ||
1 | +var h = require('hyperscript') | |
2 | +var pull = require('pull-stream') | |
3 | +var paramap = require('pull-paramap') | |
4 | +var moment = require('moment') | |
5 | + | |
6 | +var plugs = require('../plugs') | |
7 | +var message_link = plugs.first(exports.message_link = []) | |
8 | +var sbot_links = plugs.first(exports.sbot_links = []) | |
9 | +var sbot_links2 = plugs.first(exports.sbot_links2 = []) | |
10 | +var sbot_get = plugs.first(exports.sbot_get = []) | |
11 | +var getAvatar = require('ssb-avatar') | |
12 | +var avatar_name = plugs.first(exports.avatar_name = []) | |
13 | +var markdown = plugs.first(exports.markdown = []) | |
14 | + | |
15 | +var self_id = require('../keys').id | |
16 | + | |
17 | +function shortRefName(ref) { | |
18 | + return ref.replace(/^refs\/(heads|tags)\//, '') | |
19 | +} | |
20 | + | |
21 | +function repoLink(id) { | |
22 | + var el = h('a', {href: '#'+id}, id.substr(0, 10) + '…') | |
23 | + getAvatar({links: sbot_links}, self_id, id, function (err, avatar) { | |
24 | + if(err) return console.error(err) | |
25 | + el.textContent = avatar.name | |
26 | + }) | |
27 | + return el | |
28 | +} | |
29 | + | |
30 | +function getIssueState(id, cb) { | |
31 | + pull( | |
32 | + sbot_links({dest: id, rel: 'issues', values: true}), | |
33 | + pull.map(function (msg) { | |
34 | + var issues = msg.value.content.issues | |
35 | + if (!Array.isArray(issues)) return | |
36 | + return issues.filter(function (issue) { | |
37 | + return issue.link === id | |
38 | + }).map(function (issue) { | |
39 | + return { | |
40 | + ts: msg.value.timestamp, | |
41 | + open: issue.open, | |
42 | + merged: issue.merged, | |
43 | + } | |
44 | + }) | |
45 | + }), | |
46 | + pull.flatten(), | |
47 | + pull.collect(function (err, updates) { | |
48 | + if (err) return cb(err) | |
49 | + var open = true, merged = false | |
50 | + updates.sort(function (a, b) { | |
51 | + return b.ts - a.ts | |
52 | + }).forEach(function (update) { | |
53 | + if (update.open != null) | |
54 | + open = update.open | |
55 | + if (update.merged != null) | |
56 | + merged = update.merged | |
57 | + }) | |
58 | + cb(null, open ? 'open' : merged ? 'merged' : 'closed') | |
59 | + }) | |
60 | + ) | |
61 | +} | |
62 | + | |
63 | +function messageTimestampLink(msg) { | |
64 | + var m = moment(msg.value.timestamp) | |
65 | + return h('a.timestamp', { | |
66 | + timestamp: m, | |
67 | + title: m.format('LLLL'), | |
68 | + href: '#'+msg.key | |
69 | + }, m.fromNow()) | |
70 | +} | |
71 | + | |
72 | +function tableRows(headerRow) { | |
73 | + var thead = h('thead'), tbody = h('tbody') | |
74 | + var first = true | |
75 | + var t = [thead, tbody] | |
76 | + t.append = function (row) { | |
77 | + if (first) { | |
78 | + first = false | |
79 | + thead.appendChild(headerRow) | |
80 | + } | |
81 | + tbody.appendChild(row) | |
82 | + } | |
83 | + return t | |
84 | +} | |
85 | + | |
86 | +function repoName(id, link) { | |
87 | + var el = link | |
88 | + ? h('a', {href: '#'+id}, id.substr(0, 8) + '…') | |
89 | + : h('ins', id.substr(0, 8) + '…') | |
90 | + getAvatar({links: sbot_links}, self_id, id, function (err, avatar) { | |
91 | + if(err) return console.error(err) | |
92 | + el.textContent = avatar.name | |
93 | + }) | |
94 | + return el | |
95 | +} | |
96 | + | |
97 | +exports.message_content = function (msg, sbot) { | |
98 | + var c = msg.value.content | |
99 | + | |
100 | + if(c.type === 'git-repo') { | |
101 | + var nameEl | |
102 | + var branchesT, tagsT, openIssuesT, closedIssuesT, openPRsT, closedPRsT | |
103 | + var div = h('div', | |
104 | + h('p', 'git repo ', repoName(msg.key)), | |
105 | + c.upstream ? h('p', 'fork of ', repoName(c.upstream, true)) : '', | |
106 | + h('p', h('code', 'ssb://' + msg.key)), | |
107 | + h('div.git-table-wrapper', {style: {'max-height': '12em'}}, | |
108 | + h('table', | |
109 | + branchesT = tableRows(h('tr', | |
110 | + h('th', 'branch'), | |
111 | + h('th', 'commit'), | |
112 | + h('th', 'last update'))), | |
113 | + tagsT = tableRows(h('tr', | |
114 | + h('th', 'tag'), | |
115 | + h('th', 'commit'), | |
116 | + h('th', 'last update'))))), | |
117 | + h('div.git-table-wrapper', {style: {'max-height': '16em'}}, | |
118 | + h('table', | |
119 | + openIssuesT = tableRows(h('tr', | |
120 | + h('th', 'open issues'))), | |
121 | + closedIssuesT = tableRows(h('tr', | |
122 | + h('th', 'closed issues'))))), | |
123 | + h('div.git-table-wrapper', {style: {'max-height': '16em'}}, | |
124 | + h('table', | |
125 | + openPRsT = tableRows(h('tr', | |
126 | + h('th', 'open pull requests'))), | |
127 | + closedPRsT = tableRows(h('tr', | |
128 | + h('th', 'closed pull requests')))))) | |
129 | + | |
130 | + // compute refs | |
131 | + var refs = {} | |
132 | + pull( | |
133 | + sbot_links({ | |
134 | + reverse: true, | |
135 | + source: msg.value.author, | |
136 | + dest: msg.key, | |
137 | + rel: 'repo', | |
138 | + values: true | |
139 | + }), | |
140 | + pull.drain(function (link) { | |
141 | + var refUpdates = link.value.content.refs | |
142 | + for (var ref in refUpdates) { | |
143 | + if (refs[ref]) continue | |
144 | + refs[ref] = true | |
145 | + var rev = refUpdates[ref] | |
146 | + if (!rev) continue | |
147 | + var parts = /^refs\/(heads|tags)\/(.*)$/.exec(ref) || [] | |
148 | + var t | |
149 | + if (parts[1] === 'heads') t = branchesT | |
150 | + else if (parts[1] === 'tags') t = tagsT | |
151 | + if (t) t.append(h('tr', | |
152 | + h('td', parts[2]), | |
153 | + h('td', h('code', rev)), | |
154 | + h('td', messageTimestampLink(link)))) | |
155 | + } | |
156 | + }, function (err) { | |
157 | + if (err) console.error(err) | |
158 | + }) | |
159 | + ) | |
160 | + | |
161 | + // list issues and pull requests | |
162 | + pull( | |
163 | + sbot_links({ | |
164 | + reverse: true, | |
165 | + dest: msg.key, | |
166 | + rel: 'project', | |
167 | + values: true | |
168 | + }), | |
169 | + paramap(function (link, cb) { | |
170 | + getIssueState(link.key, function (err, state) { | |
171 | + if(err) return cb(err) | |
172 | + link.state = state | |
173 | + cb(null, link) | |
174 | + }) | |
175 | + }), | |
176 | + pull.drain(function (link) { | |
177 | + var c = link.value.content | |
178 | + // TODO: support renamed issues | |
179 | + var title = c.title || (c.text ? c.text.length > 30 | |
180 | + ? c.text.substr(0, 30) + '…' | |
181 | + : c.text : link.key) | |
182 | + var author = link.value.author | |
183 | + var t = c.type === 'pull-request' | |
184 | + ? link.state === 'open' ? openPRsT : closedPRsT | |
185 | + : link.state === 'open' ? openIssuesT : closedIssuesT | |
186 | + t.append(h('tr', | |
187 | + h('td', | |
188 | + h('a', {href: '#'+link.key}, title), h('br'), | |
189 | + h('small', | |
190 | + 'opened ', messageTimestampLink(link), | |
191 | + ' by ', h('a', {href: '#'+author}, avatar_name(author)))))) | |
192 | + }, function (err) { | |
193 | + if (err) console.error(err) | |
194 | + }) | |
195 | + ) | |
196 | + | |
197 | + return div | |
198 | + } | |
199 | + | |
200 | + if(c.type === 'git-update') { | |
201 | + return h('p', | |
202 | + 'pushed to ', | |
203 | + repoLink(c.repo), | |
204 | + c.refs ? h('ul', Object.keys(c.refs).map(function (ref) { | |
205 | + var rev = c.refs[ref] | |
206 | + return h('li', | |
207 | + shortRefName(ref) + ': ', | |
208 | + rev ? h('code', rev) : h('em', 'deleted')) | |
209 | + })) : null, | |
210 | + Array.isArray(c.issues) ? c.issues.map(function (issue) { | |
211 | + if (issue.merged === true) | |
212 | + return ['Merged ', message_link(issue.link), ' in ', | |
213 | + h('code', issue.object), ' ', h('q', issue.label)] | |
214 | + if (issue.open === false) | |
215 | + return ['Closed ', message_link(issue.link), ' in ', | |
216 | + h('code', issue.object), ' ', h('q', issue.label)] | |
217 | + }) : null | |
218 | + ) | |
219 | + } | |
220 | + | |
221 | + if (c.type === 'issue') { | |
222 | + return h('div', | |
223 | + h('p', 'opened issue on ', repoLink(c.project)), | |
224 | + c.title ? h('h4', c.title) : '', | |
225 | + markdown(c) | |
226 | + ) | |
227 | + } | |
228 | + | |
229 | + if (c.type === 'pull-request') { | |
230 | + return h('div', | |
231 | + h('p', 'opened pull-request ', | |
232 | + 'to ', repoLink(c.repo), ':', c.branch, ' ', | |
233 | + 'from ', repoLink(c.head_repo), ':', c.head_branch), | |
234 | + c.title ? h('h4', c.title) : '', | |
235 | + markdown(c) | |
236 | + ) | |
237 | + } | |
238 | +} | |
239 | + | |
240 | +exports.message_meta = function (msg, sbot) { | |
241 | + var type = msg.value.content.type | |
242 | + if (type == 'issue' || type == 'pull-request') { | |
243 | + var el = h('em', '...') | |
244 | + getIssueState(msg.key, function (err, state) { | |
245 | + if (err) return console.error(err) | |
246 | + el.textContent = state | |
247 | + }) | |
248 | + return el | |
249 | + } | |
250 | +} |
modules/meta-image.js | ||
---|---|---|
@@ -1,0 +1,48 @@ | ||
1 | +var markdown = require('ssb-markdown'); | |
2 | +var h = require('hyperscript'); | |
3 | +var u = require('../util'); | |
4 | +var ref = require('ssb-ref'); | |
5 | + | |
6 | +//render a message | |
7 | + | |
8 | +var plugs = require('../plugs'); | |
9 | +var message_link = plugs.first(exports.message_link = []); | |
10 | +var message_confirm = plugs.first(exports.message_confirm = []); | |
11 | +var sbot_links = plugs.first(exports.sbot_links = []); | |
12 | +var blob_url = plugs.first(exports.blob_url = []); | |
13 | + | |
14 | +exports.message_content = function(msg, sbot) { | |
15 | + if (msg.value.content.type !== 'meta-image') | |
16 | + return; | |
17 | + | |
18 | + var v = msg.value.content; | |
19 | + return h('div', | |
20 | + // h('h2', "(" + v.Track + ") " + v.Title), | |
21 | + h('img', { "src" : blob_url(v.link) })) | |
22 | + | |
23 | + // h('dl', | |
24 | + // Object.keys(v).map(function(k) { | |
25 | + // return [ | |
26 | + // h("dt", k), | |
27 | + // h("dd", v[k]), | |
28 | + // ] | |
29 | + // }))) | |
30 | + | |
31 | + // "Album": "the fall of", | |
32 | + // "Crc32": "038becab", | |
33 | + // "Creator": "bleupulp", | |
34 | + // "Format": "VBR MP3", | |
35 | + // "Height": "0", | |
36 | + // "Length": "375.23", | |
37 | + // "Md5": "2c517c8e813da5f940c8c7e77d4b7f3f", | |
38 | + // "Mtime": "1399498698", | |
39 | + // "Name": "2_bleupulp_-_clouds.mp3", | |
40 | + // "Sha1": "9f6a96a3d5571ed1ec2a7da38ffebdcd5f181482", | |
41 | + // "Size": "15009000", | |
42 | + | |
43 | + // "Title": "clouds", | |
44 | + // "Track": "2", | |
45 | + // "Width": "0", | |
46 | + | |
47 | +} | |
48 | + |
modules/music-release-cc.js | ||
---|---|---|
@@ -1,0 +1,80 @@ | ||
1 | +var markdown = require('ssb-markdown'); | |
2 | +var h = require('hyperscript'); | |
3 | +var u = require('../util'); | |
4 | +var ref = require('ssb-ref'); | |
5 | + | |
6 | +//render a message | |
7 | + | |
8 | +var plugs = require('../plugs'); | |
9 | +var message_link = plugs.first(exports.message_link = []); | |
10 | +var message_confirm = plugs.first(exports.message_confirm = []); | |
11 | +var sbot_links = plugs.first(exports.sbot_links = []); | |
12 | +var blob_url = plugs.first(exports.blob_url = []); | |
13 | + | |
14 | +exports.message_content = function(msg, sbot) { | |
15 | + if (msg.value.content.type !== 'music-release-cc') | |
16 | + return; | |
17 | + | |
18 | + var tracks = msg.value.content.tracks; | |
19 | + return h('div', | |
20 | + h('img', { "src" : blob_url(msg.value.content.cover) }), | |
21 | + h('h1', msg.value.content.title), | |
22 | + h('ol', | |
23 | + Object.keys(tracks).map(function(k) { | |
24 | + var t = tracks[k]; | |
25 | + return h('li', t.fname, | |
26 | + h("br"), | |
27 | + h('audio', { | |
28 | + "controls" : true, | |
29 | + "src" : blob_url(t.link) | |
30 | + })) | |
31 | + })), | |
32 | + h('p', | |
33 | + "More info:", h('a', { href : msg.value.content.archivedotorg }, "archive.org"), | |
34 | + h("br"), | |
35 | + "License:", h('a', { href : msg.value.content.license }, "Link"))) | |
36 | +} | |
37 | + | |
38 | +// copied from like.js | |
39 | + | |
40 | +// inspiration for waveform range selection | |
41 | + | |
42 | +// idea: handout invite codes for upload of tracks to be cached by the pub | |
43 | + | |
44 | +// exports.message_meta = function (msg, sbot) { | |
45 | + | |
46 | +// var yupps = h('a') | |
47 | + | |
48 | +// pull( | |
49 | +// sbot_links({dest: msg.key, rel: 'vote'}), | |
50 | +// pull.collect(function (err, votes) { | |
51 | +// if(votes.length === 1) | |
52 | +// yupps.textContent = ' 1 yup' | |
53 | +// if(votes.length) | |
54 | +// yupps.textContent = ' ' + votes.length + ' yupps' | |
55 | +// }) | |
56 | +// ) | |
57 | + | |
58 | +// return yupps | |
59 | +// } | |
60 | + | |
61 | +// exports.message_action = function (msg, sbot) { | |
62 | +// if(msg.value.content.type !== 'vote') | |
63 | +// return h('a', {href: '#', onclick: function () { | |
64 | +// var yup = { | |
65 | +// type: 'vote', | |
66 | +// vote: { link: msg.key, value: 1, expression: 'yup' } | |
67 | +// } | |
68 | +// if(msg.value.content.recps) { | |
69 | +// yup.recps = msg.value.content.recps.map(function (e) { | |
70 | +// return e && typeof e !== 'string' ? e.link : e | |
71 | +// }) | |
72 | +// yup.private = true | |
73 | +// } | |
74 | +// //TODO: actually publish... | |
75 | + | |
76 | +// message_confirm(yup) | |
77 | +// }}, 'yup') | |
78 | + | |
79 | +// } | |
80 | + |
modules/music-release.js | ||
---|---|---|
@@ -1,0 +1,41 @@ | ||
1 | +var markdown = require('ssb-markdown'); | |
2 | +var h = require('hyperscript'); | |
3 | +var u = require('../util'); | |
4 | +var ref = require('ssb-ref'); | |
5 | + | |
6 | +//render a message | |
7 | + | |
8 | +var plugs = require('../plugs'); | |
9 | +var message_link = plugs.first(exports.message_link = []); | |
10 | +var message_confirm = plugs.first(exports.message_confirm = []); | |
11 | +var sbot_links = plugs.first(exports.sbot_links = []); | |
12 | + | |
13 | +exports.message_content = function(msg, sbot) { | |
14 | + if (msg.value.content.type !== 'music-release') | |
15 | + return; | |
16 | + | |
17 | + var v = msg.value.content; | |
18 | + return h('div', | |
19 | + // h('img', { "src" : "http://localhost:7777/" + encodeURIComponent(v.cover) }), | |
20 | + h('h1', v.Title), | |
21 | + h("p", v.Description), | |
22 | + h("dl", | |
23 | + | |
24 | + h("dt", "Creator"), | |
25 | + h("dd", v.Creator), | |
26 | + | |
27 | + h("dt", "Identifier"), | |
28 | + h("dd", v.Identifier), | |
29 | + | |
30 | + h("dt", "Published"), | |
31 | + h("dd", v.Publicdate), | |
32 | + | |
33 | + h("dt", "Runtime"), | |
34 | + h("dd", v.Runtime), | |
35 | + | |
36 | + h("dt", "Source"), | |
37 | + h("dd", v.Source), | |
38 | + | |
39 | + h("dt", "License"), | |
40 | + h("dd", h('a', { href : v.Licenseurl }, "Link")))) | |
41 | +} |
modules/relationships.js | ||
---|---|---|
@@ -1,0 +1,23 @@ | ||
1 | + | |
2 | +var plugs = require('../plugs') | |
3 | + | |
4 | +var sbot_links2 = plugs.first(exports.sbot_links2 = []) | |
5 | + | |
6 | +//this is a bit crude, and doesn't actually show unfollows yet. | |
7 | + | |
8 | +exports.follows = function (id, cb) { | |
9 | + return sbot_links2({query: [ | |
10 | + {"$filter": {"source": id, "rel": ["contact", true, false] }}, | |
11 | + {"$map": "dest"} | |
12 | + ]}) | |
13 | +} | |
14 | + | |
15 | +exports.followers = function (id) { | |
16 | + return sbot_links2({query: [ | |
17 | + {"$filter": {"dest": id, "rel": ["contact", true, false] }}, | |
18 | + {"$map": "source"} | |
19 | + ]}) | |
20 | +} | |
21 | + | |
22 | + | |
23 | + |
package.json | ||
---|---|---|
@@ -1,22 +1,27 @@ | ||
1 | 1 | { |
2 | 2 | "name": "patchbay", |
3 | 3 | "description": "a pluggable patchwork", |
4 | - "version": "1.8.4", | |
4 | + "version": "1.13.3", | |
5 | 5 | "homepage": "https://github.com/dominictarr/patchbay", |
6 | 6 | "repository": { |
7 | 7 | "type": "git", |
8 | 8 | "url": "git://github.com/dominictarr/patchbay.git" |
9 | 9 | }, |
10 | 10 | "dependencies": { |
11 | 11 | "cont": "^1.0.3", |
12 | + "dataurl": "^0.1.0", | |
12 | 13 | "depject": "^1.0.1", |
13 | - "hyperlightbox": "^0.1.0", | |
14 | + "hypercrop": "^1.0.1", | |
15 | + "hyperfile": "^1.1.0", | |
16 | + "hyperlightbox": "^0.1.1", | |
17 | + "hyperprogress": "0.1.0", | |
14 | 18 | "hyperscript": "^1.4.7", |
15 | 19 | "hypertabs": "^1.2.0", |
16 | 20 | "mime-types": "^2.1.11", |
17 | 21 | "moment": "^2.13.0", |
18 | 22 | "open-external": "^0.1.1", |
23 | + "peaks.js": "^0.4.7", | |
19 | 24 | "pull-cat": "^1.1.9", |
20 | 25 | "pull-next": "0.0.0", |
21 | 26 | "pull-paramap": "^1.1.6", |
22 | 27 | "pull-reconnect": "^0.0.3", |
sbot-api.js | ||
---|---|---|
@@ -75,9 +75,22 @@ | ||
75 | 75 | return { |
76 | 76 | connection_status: connection_status, |
77 | 77 | sbot_blobs_add: rec.sink(function (cb) { |
78 | 78 | return pull( |
79 | - Hash(cb), | |
79 | + Hash(function (err, id) { | |
80 | + if(err) return cb(err) | |
81 | + //completely UGLY hack to tell when the blob has been sucessfully written... | |
82 | + var start = Date.now(), n = 5 | |
83 | + ;(function next () { | |
84 | + setTimeout(function () { | |
85 | + sbot.blobs.has(id, function (err, has) { | |
86 | + if(has) return cb(null, id) | |
87 | + if(n--) next() | |
88 | + else cb(new Error('write failed')) | |
89 | + }) | |
90 | + }, Date.now() - start) | |
91 | + })() | |
92 | + }), | |
80 | 93 | sbot.blobs.add() |
81 | 94 | ) |
82 | 95 | }), |
83 | 96 | sbot_links: rec.source(function (query) { |
@@ -102,8 +115,16 @@ | ||
102 | 115 | if(content.recps) |
103 | 116 | content = ssbKeys.box(content, content.recps.map(function (e) { |
104 | 117 | return ref.isFeed(e) ? e : e.link |
105 | 118 | })) |
119 | + else if(content.mentions) | |
120 | + content.mentions.forEach(function (mention) { | |
121 | + if(ref.isBlob(mention.link)) { | |
122 | + sbot.blobs.push(mention.link, function (err) { | |
123 | + if(err) console.error(err) | |
124 | + }) | |
125 | + } | |
126 | + }) | |
106 | 127 | |
107 | 128 | feed.add(content, function (err, msg) { |
108 | 129 | if(err) console.error(err) |
109 | 130 | else if(!cb) console.log(msg) |
style.css | ||
---|---|---|
@@ -1,8 +1,10 @@ | ||
1 | 1 | body { |
2 | 2 | font-family: "Source Sans Pro", sans-serif; |
3 | 3 | } |
4 | 4 | |
5 | +p { margin-top: .35ex;} | |
6 | + | |
5 | 7 | .screen { |
6 | 8 | position: absolute; |
7 | 9 | top: 0px; bottom: 0px; |
8 | 10 | left: 0px; right: 0px; |
@@ -20,8 +22,14 @@ | ||
20 | 22 | display: flex; |
21 | 23 | flex-direction: row; |
22 | 24 | } |
23 | 25 | |
26 | +.wrap { | |
27 | + display: flex; | |
28 | + flex-direction: row; | |
29 | + flex-wrap: wrap; | |
30 | +} | |
31 | + | |
24 | 32 | .stretch { |
25 | 33 | flex-basis: 0; |
26 | 34 | } |
27 | 35 | |
@@ -44,9 +52,9 @@ | ||
44 | 52 | overflow-x: hidden; |
45 | 53 | } |
46 | 54 | |
47 | 55 | |
48 | -img { | |
56 | +.message img { | |
49 | 57 | max-width: 100%; |
50 | 58 | display: block; |
51 | 59 | } |
52 | 60 | |
@@ -125,9 +133,9 @@ | ||
125 | 133 | /* messages */ |
126 | 134 | |
127 | 135 | .message { |
128 | 136 | border: 1px solid #eee; |
129 | - padding: 5px; | |
137 | + padding: 7px; | |
130 | 138 | margin-top: .5em; |
131 | 139 | background: white; |
132 | 140 | display: block; |
133 | 141 | flex-basis: 0; |
@@ -203,26 +211,34 @@ | ||
203 | 211 | .avatar { |
204 | 212 | display: flex; |
205 | 213 | flex-direction: row; |
206 | 214 | } |
207 | -.avatar img { | |
215 | + | |
216 | +.avatar--large { | |
217 | + width: 256px; | |
218 | + height: 256px; | |
219 | + border: 1px solid #eee; | |
220 | +} | |
221 | + | |
222 | +.avatar--thumbnail { | |
208 | 223 | width: 40px; |
209 | 224 | height: 40px; |
210 | 225 | margin-right: 3px; |
211 | 226 | border: 1px solid #ccc; |
212 | 227 | } |
213 | 228 | |
229 | +.avatar--fullsize { | |
230 | + width: 100%; | |
231 | +} | |
232 | + | |
214 | 233 | .profile { |
215 | 234 | background: #fff; |
235 | + padding: .5em; | |
216 | 236 | border: 1px solid #eee; |
217 | 237 | } |
218 | 238 | |
219 | -.profile img { | |
220 | - width: 150px; | |
221 | - height: 150px; | |
222 | - margin-right: 1em; | |
223 | - margin-bottom: 1em; | |
224 | - border: 1px solid #ccc; | |
239 | +.profile__info { | |
240 | + margin-left: .5em; | |
225 | 241 | } |
226 | 242 | |
227 | 243 | /* lightbox - used in message-confirm */ |
228 | 244 | |
@@ -278,6 +294,35 @@ | ||
278 | 294 | .error { |
279 | 295 | background: red; |
280 | 296 | } |
281 | 297 | |
298 | +/* avatar editor */ | |
282 | 299 | |
300 | +.hypercrop__canvas { | |
301 | + width: 100%; | |
302 | +} | |
283 | 303 | |
304 | +/* gitssb */ | |
305 | + | |
306 | +.git-table-wrapper { | |
307 | + max-height: 12em; | |
308 | + overflow: auto; | |
309 | + word-break: break-all; | |
310 | + margin: 1em 0; | |
311 | +} | |
312 | + | |
313 | +.git-table-wrapper table { | |
314 | + width: 100%; | |
315 | +} | |
316 | + | |
317 | +/* invite codes */ | |
318 | + | |
319 | +.hyperprogress__liquid { | |
320 | + height: 1px; | |
321 | + background: blue; | |
322 | +} | |
323 | + | |
324 | + | |
325 | + | |
326 | + | |
327 | + | |
328 | + |
Built with git-ssb-web