Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,134 @@
html, body {
padding: 0;
margin: 0;
font-family: sans-serif;
width: 100%;
height: 100%;
overflow: hidden;
}
h1 {
pointer-events: none;
}
#h1-after {
position: absolute;
width: 100%;
height: 4px;
left: 0;
top: 46px;
pointer-events: none;
background-color: #ff9500;
opacity: 0;
animation-duration: 0.25s;
animation-timing-function: ease-in;
animation-fill-mode: both;
-webkit-animation-duration: 0.25s;
-webkit-animation-timing-function: ease-in;
-webkit-animation-fill-mode: both;
}
[data-anim='reveal'] {
animation-name: reveal;
-webkit-animation-name: reveal;
}
@keyframes reveal {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@-webkit-keyframes reveal {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
[data-anim='hide'] {
animation-name: hide;
-webkit-animation-name: hide;
}
@keyframes hide {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@-webkit-keyframes hide {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
button {
position: absolute;
display: block;
top: 0;
right: 0;
height: 50px;
border: 0;
border-radius: 0;
outline: 0;
background: none;
padding: 0 15px;
text-align: center;
text-decoration: underline;
font-size: 14px;
color: #ffffff;
-moz-appearance: none;
opacity: 1;
transition: transform 0.1s linear,
opacity 0.1s linear;
-webkit-transition: -webkit-transform 0.1s linear,
opacity 0.1s linear;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
button.transitioning {
opacity: 0.5;
transform: scale(0.6, 0.6);
-webkit-transform: scale(0.6, 0.6);
pointer-events: none;
}
ul {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
body > section {
position: absolute;
top: 50px;
width: 100%;
height: calc(100% - 50px);
-webkit-overflow-scrolling: touch;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}

View file

@ -0,0 +1,21 @@
h1 {
position: absolute;
top: 0;
left: 0;
width: 100%;
margin: 0;
padding: 0;
background: #27c8c2;
text-align: center;
line-height: 50px;
font-weight: 300;
font-style: italic;
font-size: 24px;
color: #ffffff;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}

View file

@ -0,0 +1,143 @@
ul {
list-style: none;
padding: 0 0 0 0;
margin: 0;
}
ul section {
margin: 0;
padding: 0;
}
ul h2 {
position: sticky;
position: -webkit-sticky;
top: 0px;
height: 32px;
margin: 0;
padding: 0 6px;
box-sizing: border-box;
font-size: 0.9em;
line-height: 32px;
background: #efefef;
border-bottom: 1px #bcbcbc solid;
z-index: 100;
}
ul .background {
position: absolute;
width: 100%;
background: linear-gradient(to bottom, transparent 0px, transparent 45px, #EDEDED 45px, #EDEDED 70px, transparent 20px),
linear-gradient(to bottom, transparent 0px, transparent 15px, #EDEDED 15px, #EDEDED 25px, transparent 20px),
linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white);
background-position: 20% 88px, 5% -88px, 0 -88px;
background-size: 90% 88px, 60% 88px, 100% 88px;
background-repeat: repeat-y;
z-index: 0;
}
ul.reordering .background {
background: linear-gradient(to top, #bcbcbc, #bcbcbc 1px, white 1px, white);
background-position: 0 -88px;
background-size: 100% 88px;
}
ul li {
width: 100%;
height: 88px;
margin: 0;
padding: 0;
box-sizing: border-box;
-moz-user-select: none;
background: white;
border-bottom: 1px #bcbcbc solid;
z-index: 10;
}
ul li h3 {
margin: 0;
padding: 9px 6px 3px 6px;
background: white;
color: #424242;
pointer-events: none;
}
ul li p {
margin: 0;
padding: 3px 6px 10px 6px;
background: white;
color: #797979;
pointer-events: none;
}
li.new {
opacity: 0;
}
li .overlay {
position: absolute;
top: 1px;
left: 0;
width: 100%;
height: calc(100% - 2px);
background: rgba(255, 255, 255, 0.7);
opacity: 0;
animation-duration: 0.2s;
animation-timing-function: ease-in;
animation-fill-mode: both;
-webkit-animation-duration: 0.2s;
-webkit-animation-timing-function: ease-in;
-webkit-animation-fill-mode: both;
pointer-events: none;
}
li.edit .overlay {
opacity: 1;
pointer-events: auto;
}
.overlay .cursor {
margin: 17px auto;
padding: 0;
width: 50px;
height: 50px;
text-align: center;
line-height: 50px;
font-size: 25px;
color: #0b0b0b;
border-radius: 8px;
background: rgba(0, 0, 0, 0.3);
-moz-user-select: none;
}
li.dragging {
transition: transform 0s linear;
-webkit-transition: -webkit-transform 0s linear;
}
li.dragging .overlay {
opacity: 0;
}
li[data-populated=false] {
opacity: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
<link rel="stylesheet" href="css/h1.css">
<link rel="stylesheet" href="css/ul.css">
<link rel="stylesheet" href="css/app.css">
<!-- Component Style -->
<link rel="stylesheet" type="text/css" href="bower_components/base/base.css">
<link rel="stylesheet" type="text/css" href="bower_components/gaia-theme/gaia-theme.css">
<link rel="stylesheet" type="text/css" href="bower_components/gaia-fonts/style.css">
<link rel="stylesheet" type="text/css" href="bower_components/gaia-icons/gaia-icons.css">
<!-- Dependencies -->
<script type="text/javascript" src="lib/dom-scheduler.js" defer></script>
<script src="bower_components/gaia-component/gaia-component.js" defer></script>
<script src="bower_components/gaia-button/gaia-button.js" defer></script>
<script src="bower_components/gaia-text-input/gaia-text-input.js" defer></script>
<!-- Libs -->
<script src="bower_components/gaia-dialog/gaia-dialog.js" defer></script>
<script src="bower_components/gaia-dialog/gaia-dialog-alert.js" defer></script>
<script src="bower_components/fast-list/fast-list.js" defer></script>
<!-- App -->
<script type="text/javascript" src="js/bacon-source.js" async></script>
<script type="text/javascript" src="js/app.js" async></script>
<title>Dom Scheduler Demo</title>
</head>
<body>
<h1>
Main List
</h1>
<div id="h1-after"></div>
<button>
Edit
</button>
<section>
<ul>
<section
><h2> </h2
><div class="background"
></div
></section>
<li
><h3> </h3
><p> </p
><div class="overlay"
><div class="cursor">↕︎</div></div
></li>
</ul>
</section>
<gaia-dialog-alert id="mine">List item clicked!</gaia-dialog-alert>
</body>
</html>

View file

@ -0,0 +1,95 @@
(function() {
var debug = false;
var headerHeight = 50;
var maxItemCount = 28;
var endEvent = ('ontouchstart' in window) ? 'touchend' : 'mouseup';
window.addEventListener('load', function() {
var listContainer = document.querySelector('section');
var source = new BaconSource();
var list = new FastList(listContainer, source);
var dialog = document.querySelector('gaia-dialog-alert');
function updateHeader() {
return scheduler.mutation(function() {
var h1 = document.querySelector('h1');
h1.textContent = 'Main List (' + source.fullLength() + ')';
h1.scrollTop; // flush
});
}
updateHeader();
function clearNewIndicator() {
var h1After = document.querySelector('#h1-after');
if (h1After.dataset.anim == 'reveal') {
scheduler.transition(function() {
h1After.dataset.anim = 'hide';
}, h1After, 'animationend');
}
}
listContainer.addEventListener('top-reached', clearNewIndicator);
function updateNewIndicator() {
var h1After = document.querySelector('#h1-after');
if (h1After.dataset.anim == 'reveal') {
return;
}
scheduler.transition(function() {
h1After.dataset.anim = 'reveal';
}, h1After, 'animationend');
}
listContainer.addEventListener('hidden-new-content', updateNewIndicator);
function openGaiaDialog(evt) {
scheduler.mutation(function() {
var detail = evt.detail;
var li = source.getRecordAt(detail.index);
dialog.textContent = li.title + ' item clicked!';
dialog.open(detail.clickEvt);
});
}
list.list.addEventListener('item-selected', openGaiaDialog);
function newContentHandler() {
var newContent = {
title: 'NEW Bacon ' + Date.now().toString().slice(7, -1),
body: 'Turkey BLT please.'
};
source.insertAtIndex(0, newContent);
list.insertedAtIndex(0);
updateHeader();
}
setInterval(newContentHandler, 15000);
window.addEventListener('new-content', newContentHandler);
window.pushNewContent = function() {
window.dispatchEvent(new CustomEvent('new-content'));
};
var button = document.querySelector('button');
button.addEventListener(endEvent, function() {
Promise.all([toggleTransitioning(), list.toggleEditMode()])
.then(updateText)
.then(toggleTransitioning);
});
function updateText(text) {
return scheduler.mutation(function() {
button.textContent = list.editing ? 'Exit' : 'Edit';
});
}
function toggleTransitioning() {
return scheduler.feedback(function() {
button.classList.toggle('transitioning');
}, button, 'transitionend');
}
});
})();

View file

@ -0,0 +1,175 @@
(function(exports) {
var listSize = 1042;
var sectionCount = 25;
var headerHeight = 32;
var itemHeight = 88;
function makeContent(prefix, name) {
return {
title: prefix + ' Bacon ipsum dolor ' +
Date.now().toString().slice(7, -1),
body: 'Turkey pig boudin ' + name.toLowerCase() +
' de mignon drums and all ' + prefix + '.'
}
}
exports.BaconSource = function BaconSource() {
this.sections = [];
this.contentMap = new Map();
this._cachedLength = null;
for (var i = 0; i < sectionCount; i++) {
this.sections.push('Section ' + i);
}
this.sections.forEach(function(name) {
var content = [];
for (var i = 0; i < (Math.random() * listSize); i++) {
content.push(makeContent(i, name));
}
this.contentMap.set(name, content);
}, this);
};
exports.BaconSource.prototype = {
populateItem: function(item, i) {
// Simulates the data source not being ready to populate
/*if (parseInt(Date.now() / 10) % 8 === 0) {*/
/*return new Promise(function(resolve, reject) {*/
/*setTimeout(resolve, Math.random() * 1000);*/
/*});*/
/*}*/
var title = item.firstChild;
var body = title.nextSibling;
var record = this.getRecordAt(i);
title.firstChild.data = record.title;
body.firstChild.data = record.body;
},
getSections: function() {
return this.contentMap.keys();
},
sectionHeaderHeight: function() {
return headerHeight;
},
fullSectionHeight: function(key) {
return this.contentMap.get(key).length * itemHeight;
},
fullSectionLength: function(key) {
return this.contentMap(key).length;
},
getRecordAt: function(index) {
for (var section of this.contentMap.values()) {
if (index < section.length) {
return section[index];
}
index -= section.length;
}
},
getSectionFor: function(index) {
for (var entry of this.contentMap.entries()) {
if (index < entry[1].length) {
return entry[0];
}
index -= entry[1].length;
}
},
indexAtPosition: function(pos) {
var index = 0;
for (var section of this.contentMap.values()) {
pos -= headerHeight;
var sectionHeight = section.length * itemHeight;
if (pos > sectionHeight) {
pos -= sectionHeight;
index += section.length;
continue;
}
for (var item of section) {
pos -= itemHeight;
index++;
if (pos <= 0 || index === this.fullLength() - 1) {
return index;
}
}
}
},
positionForIndex: function(index) {
var top = 0;
for (var section of this.contentMap.values()) {
top += headerHeight;
if (index < section.length) {
top += index * itemHeight;
return top;
}
index -= section.length;
top += section.length * itemHeight;
}
},
fullLength: function() {
if (this._cachedLength) {
return this._cachedLength;
}
var length = 0;
for (var section of this.contentMap.values()) {
length += section.length;
}
this._cachedLength = length;
return length;
},
itemHeight: function() {
return itemHeight;
},
fullHeight: function() {
var height = 0;
for (var section of this.contentMap.values()) {
height += headerHeight + section.length * itemHeight;
}
return height;
},
insertAtIndex: function(index, record, toSection) {
this._cachedLength = null;
for (var entry of this.contentMap.entries()) {
if (index < entry[1].length || entry[0] === toSection) {
return entry[1].splice(index, 0, record);
}
index -= entry[1].length;
}
},
replaceAtIndex: function(index, record) {
for (var section of this.contentMap.values()) {
if (index < section.length) {
return section.splice(index, 1, record);
}
index -= section.length;
}
},
removeAtIndex: function(index) {
this._cachedLength = null;
for (var section of this.contentMap.values()) {
if (index < section.length) {
return section.splice(index, 1)[0];
}
index -= section.length;
}
}
};
})(window);

View file

@ -0,0 +1,14 @@
{
"name": "dom-scheduler-demo",
"description": "A Hello World app",
"launch_path": "/index.html",
"icons": {
"16": "/icons/icon16x16.png",
"48": "/icons/icon48x48.png",
"60": "/icons/icon60x60.png",
"128": "/icons/icon128x128.png"
},
"type": "certified",
"orientation": "default",
"permissions": {}
}