insert rows from viewer

This commit is contained in:
Brent Schroeter 2025-08-13 18:52:37 -07:00
parent dc82afe00d
commit 07d4987f3c
1304 changed files with 403 additions and 204 deletions

View file

@ -5,7 +5,7 @@
"imports": { "imports": {
"@std/assert": "jsr:@std/assert@1", "@std/assert": "jsr:@std/assert@1",
"@std/path": "jsr:@std/path@^1.1.1", "@std/path": "jsr:@std/path@^1.1.1",
"@std/uuid": "jsr:@std/uuid@^1.0.9", "@std/uuid": "jsr:@std/uuid@^1.0.9"
}, },
"unstable": ["fmt-component"] "unstable": ["fmt-component"]
} }

View file

@ -10,7 +10,6 @@
"jsr:@std/uuid@*": "1.0.9", "jsr:@std/uuid@*": "1.0.9",
"jsr:@std/uuid@^1.0.9": "1.0.9", "jsr:@std/uuid@^1.0.9": "1.0.9",
"npm:@deno/vite-plugin@^1.0.5": "1.0.5_vite@7.1.2__picomatch@4.0.3", "npm:@deno/vite-plugin@^1.0.5": "1.0.5_vite@7.1.2__picomatch@4.0.3",
"npm:@jsr/std__uuid@^1.0.9": "1.0.9",
"npm:@sveltejs/vite-plugin-svelte@^6.1.1": "6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3", "npm:@sveltejs/vite-plugin-svelte@^6.1.1": "6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3",
"npm:@tsconfig/svelte@^5.0.4": "5.0.4", "npm:@tsconfig/svelte@^5.0.4": "5.0.4",
"npm:svelte-check@^4.3.1": "4.3.1_svelte@5.38.1__acorn@8.15.0_typescript@5.8.3", "npm:svelte-check@^4.3.1": "4.3.1_svelte@5.38.1__acorn@8.15.0_typescript@5.8.3",
@ -370,22 +369,6 @@
"@jridgewell/sourcemap-codec" "@jridgewell/sourcemap-codec"
] ]
}, },
"@jsr/std__bytes@1.0.6": {
"integrity": "sha512-St6yKggjFGhxS52IFLJWvkchRFbAKg2Xh8UxA4S1EGz7GJ2Ui+ssDDldj/w2c8vCxvl6qgR0HaYbKeFJNqujmA==",
"tarball": "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.6.tgz"
},
"@jsr/std__crypto@1.0.5": {
"integrity": "sha512-iqFCkjeGeQccLgmxH9m1d7abjZcFMW0XrYZu1itNz8vVHzH9crObalonjVQaVDdKHCrNNOklMN1t0u3k46dirA==",
"tarball": "https://npm.jsr.io/~/11/@jsr/std__crypto/1.0.5.tgz"
},
"@jsr/std__uuid@1.0.9": {
"integrity": "sha512-hyTTOsmUTnLuah/OMnkYfZ8fSqYbB113idHZfIYlhN26PdpMGTqTUZkFCA1Q6oBhW8d8N82RG0PnRohQ0peqhA==",
"dependencies": [
"@jsr/std__bytes",
"@jsr/std__crypto"
],
"tarball": "https://npm.jsr.io/~/11/@jsr/std__uuid/1.0.9.tgz"
},
"@rollup/rollup-android-arm-eabi@4.46.2": { "@rollup/rollup-android-arm-eabi@4.46.2": {
"integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
"os": ["android"], "os": ["android"],
@ -996,7 +979,6 @@
"packageJson": { "packageJson": {
"dependencies": [ "dependencies": [
"npm:@deno/vite-plugin@^1.0.5", "npm:@deno/vite-plugin@^1.0.5",
"npm:@jsr/std__uuid@^1.0.9",
"npm:@sveltejs/vite-plugin-svelte@^6.1.1", "npm:@sveltejs/vite-plugin-svelte@^6.1.1",
"npm:@tsconfig/svelte@^5.0.4", "npm:@tsconfig/svelte@^5.0.4",
"npm:svelte-check@^4.3.1", "npm:svelte-check@^4.3.1",

View file

@ -143,16 +143,6 @@ impl FieldType {
Self::Unknown => None, Self::Unknown => None,
} }
} }
pub fn default_for_insert(&self) -> Result<Encodable, FieldTypeUnknownError> {
match self {
Self::InterimUser {} => Ok(Encodable::Text(None)),
Self::Text {} => Ok(Encodable::Text(None)),
Self::Timestamp { .. } => Ok(Encodable::Timestamp(None)),
Self::Uuid { .. } => Ok(Encodable::Uuid(None)),
Self::Unknown => Err(FieldTypeUnknownError {}),
}
}
} }
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
@ -235,7 +225,7 @@ pub enum Encodable {
impl Encodable { impl Encodable {
pub fn bind_onto<'a>( pub fn bind_onto<'a>(
&'a self, self,
query: sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>>, query: sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>>,
) -> sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>> { ) -> sqlx::query::Query<'a, Postgres, <sqlx::Postgres as sqlx::Database>::Arguments<'a>> {
match self { match self {
@ -255,4 +245,11 @@ impl Encodable {
let deserialized: Tagged = serde_json::from_value(serialized).unwrap(); let deserialized: Tagged = serde_json::from_value(serialized).unwrap();
deserialized.c deserialized.c
} }
pub fn is_none(&self) -> bool {
match self {
Self::Text(None) | Self::Timestamp(None) | Self::Uuid(None) => true,
Self::Text(_) | Self::Timestamp(_) | Self::Uuid(_) => false,
}
}
} }

View file

@ -0,0 +1,9 @@
use interim_models::field::Field;
use serde::Serialize;
#[derive(Clone, Debug, Serialize)]
pub struct FieldInfo {
pub field: Field,
pub has_default: bool,
pub not_null: bool,
}

View file

@ -15,6 +15,7 @@ mod auth;
mod base_pooler; mod base_pooler;
mod base_user_perms; mod base_user_perms;
mod cli; mod cli;
mod field;
mod middleware; mod middleware;
mod navbar; mod navbar;
mod navigator; mod navigator;

View file

@ -81,6 +81,10 @@ pub fn new_router(state: AppState) -> Router<()> {
"/d/{base_id}/r/{class_oid}/l/{lens_id}/update-value", "/d/{base_id}/r/{class_oid}/l/{lens_id}/update-value",
post(routes::lenses::update_value_page_post), post(routes::lenses::update_value_page_post),
) )
.route(
"/d/{base_id}/r/{class_oid}/l/{lens_id}/insert",
post(routes::lens_insert::insert_page_post),
)
.route_with_tsr( .route_with_tsr(
"/d/{base_id}/r/{class_oid}/l/{lens_id}/viewer/", "/d/{base_id}/r/{class_oid}/l/{lens_id}/viewer/",
get(routes::lenses::viewer_page), get(routes::lenses::viewer_page),

View file

@ -0,0 +1,95 @@
use std::collections::HashMap;
use axum::{
extract::{Path, State},
response::Response,
};
use axum_extra::extract::Form;
use interim_models::{field::Encodable, lens::Lens};
use interim_pgtypes::{escape_identifier, pg_class::PgClass};
use sqlx::{postgres::types::Oid, query};
use crate::{
app_error::AppError,
app_state::AppDbConn,
base_pooler::{BasePooler, RoleAssignment},
navigator::Navigator,
user::CurrentUser,
};
use super::LensPagePath;
pub async fn insert_page_post(
State(mut base_pooler): State<BasePooler>,
navigator: Navigator,
AppDbConn(mut app_db): AppDbConn,
CurrentUser(current_user): CurrentUser,
Path(LensPagePath {
base_id,
class_oid,
lens_id,
}): Path<LensPagePath>,
Form(body): Form<HashMap<String, Vec<String>>>,
) -> Result<Response, AppError> {
// FIXME auth, csrf
let lens = Lens::with_id(lens_id).fetch_one(&mut app_db).await?;
let mut base_client = base_pooler
.acquire_for(base_id, RoleAssignment::User(current_user.id))
.await?;
let rel = PgClass::with_oid(Oid(class_oid))
.fetch_one(&mut base_client)
.await?;
let col_names: Vec<String> = body.keys().cloned().collect();
let col_list_sql = col_names
.iter()
.map(|value| escape_identifier(value))
.collect::<Vec<String>>()
.join(", ");
let n_rows = body.values().map(|value| value.len()).max().unwrap_or(0);
if n_rows > 0 {
let mut param_index = 1;
let mut params: Vec<Encodable> = vec![];
let mut row_list: Vec<String> = vec![];
for i in 0..n_rows {
let mut param_slots: Vec<String> = vec![];
for col in col_names.iter() {
let maybe_value: Option<Encodable> = body
.get(col)
.and_then(|col_values| col_values.get(i))
.map(|value_raw| serde_json::from_str(value_raw))
.transpose()?;
if let Some(value) = maybe_value.filter(|value| !value.is_none()) {
params.push(value);
param_slots.push(format!("${param_index}"));
param_index += 1;
} else {
param_slots.push("default".to_owned());
}
}
row_list.push(format!("({})", param_slots.join(", ")));
}
let row_list_sql = row_list.join(",\n");
let query_sql = &format!(
r#"
insert into {0}.{1}
({col_list_sql})
values
{row_list_sql}
"#,
escape_identifier(&rel.regnamespace),
escape_identifier(&rel.relname)
);
let mut q = query(query_sql);
for param in params {
q = param.bind_onto(q);
}
q.execute(base_client.get_conn()).await?;
}
Ok(navigator.lens_page(&lens).redirect_to())
}

View file

@ -25,12 +25,15 @@ use crate::{
app_error::{AppError, bad_request}, app_error::{AppError, bad_request},
app_state::AppDbConn, app_state::AppDbConn,
base_pooler::{BasePooler, RoleAssignment}, base_pooler::{BasePooler, RoleAssignment},
field::FieldInfo,
navbar::{NavLocation, Navbar, RelLocation}, navbar::{NavLocation, Navbar, RelLocation},
navigator::Navigator, navigator::Navigator,
settings::Settings, settings::Settings,
user::CurrentUser, user::CurrentUser,
}; };
use super::LensPagePath;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct LensesPagePath { pub struct LensesPagePath {
base_id: Uuid, base_id: Uuid,
@ -138,13 +141,6 @@ pub async fn add_lens_page_post(
Ok(navigator.lens_page(&lens).redirect_to()) Ok(navigator.lens_page(&lens).redirect_to())
} }
#[derive(Deserialize)]
pub struct LensPagePath {
base_id: Uuid,
class_oid: u32,
lens_id: Uuid,
}
pub async fn lens_page( pub async fn lens_page(
State(settings): State<Settings>, State(settings): State<Settings>,
State(mut base_pooler): State<BasePooler>, State(mut base_pooler): State<BasePooler>,
@ -163,63 +159,15 @@ pub async fn lens_page(
let mut base_client = base_pooler let mut base_client = base_pooler
.acquire_for(lens.base_id, RoleAssignment::User(current_user.id)) .acquire_for(lens.base_id, RoleAssignment::User(current_user.id))
.await?; .await?;
let rel = PgClass::with_oid(lens.class_oid)
.fetch_one(&mut base_client)
.await?;
let attrs = PgAttribute::all_for_rel(lens.class_oid)
.fetch_all(&mut base_client)
.await?;
let fields = Field::belonging_to_lens(lens.id)
.fetch_all(&mut app_db)
.await?;
let pkey_attrs = PgAttribute::pkeys_for_rel(lens.class_oid)
.fetch_all(&mut base_client)
.await?;
const FRONTEND_ROW_LIMIT: i64 = 1000;
let rows: Vec<PgRow> = query(&format!(
"select {0} from {1}.{2} limit $1",
pkey_attrs
.iter()
.chain(attrs.iter())
.map(|attr| escape_identifier(&attr.attname))
.collect::<Vec<_>>()
.join(", "),
escape_identifier(&rel.regnamespace),
escape_identifier(&rel.relname),
))
.bind(FRONTEND_ROW_LIMIT)
.fetch_all(base_client.get_conn())
.await?;
let pkeys: Vec<HashMap<String, Encodable>> = rows
.iter()
.map(|row| {
let mut pkey_values: HashMap<String, Encodable> = HashMap::new();
for attr in pkey_attrs.clone() {
let field = Field::default_from_attr(&attr);
pkey_values.insert(field.name.clone(), field.get_value_encodable(row).unwrap());
}
pkey_values
})
.collect();
#[derive(Template)] #[derive(Template)]
#[template(path = "lens0_2.html")] #[template(path = "lens0_2.html")]
struct ResponseTemplate { struct ResponseTemplate {
fields: Vec<Field>,
all_columns: Vec<PgAttribute>,
rows: Vec<PgRow>,
pkeys: Vec<HashMap<String, Encodable>>,
settings: Settings, settings: Settings,
navbar: Navbar, navbar: Navbar,
} }
Ok(Html( Ok(Html(
ResponseTemplate { ResponseTemplate {
all_columns: attrs,
fields,
pkeys,
rows,
navbar: Navbar::builder() navbar: Navbar::builder()
.root_path(settings.root_path.clone()) .root_path(settings.root_path.clone())
.base(base.clone()) .base(base.clone())
@ -262,13 +210,27 @@ pub async fn get_data_page_get(
let attrs = PgAttribute::all_for_rel(lens.class_oid) let attrs = PgAttribute::all_for_rel(lens.class_oid)
.fetch_all(&mut base_client) .fetch_all(&mut base_client)
.await?; .await?;
let fields = Field::belonging_to_lens(lens.id)
.fetch_all(&mut app_db)
.await?;
let pkey_attrs = PgAttribute::pkeys_for_rel(lens.class_oid) let pkey_attrs = PgAttribute::pkeys_for_rel(lens.class_oid)
.fetch_all(&mut base_client) .fetch_all(&mut base_client)
.await?; .await?;
let fields: Vec<FieldInfo> = {
let fields: Vec<Field> = Field::belonging_to_lens(lens.id)
.fetch_all(&mut app_db)
.await?;
let mut field_info: Vec<FieldInfo> = Vec::with_capacity(fields.len());
for field in fields {
if let Some(attr) = attrs.iter().find(|attr| attr.attname == field.name) {
field_info.push(FieldInfo {
field,
has_default: attr.atthasdef,
not_null: attr.attnotnull.unwrap_or_default(),
});
}
}
field_info
};
const FRONTEND_ROW_LIMIT: i64 = 1000; const FRONTEND_ROW_LIMIT: i64 = 1000;
let rows: Vec<PgRow> = query(&format!( let rows: Vec<PgRow> = query(&format!(
"select {0} from {1}.{2} limit $1", "select {0} from {1}.{2} limit $1",
@ -291,7 +253,7 @@ pub async fn get_data_page_get(
data: Vec<Encodable>, data: Vec<Encodable>,
} }
let mut data: Vec<DataRow> = vec![]; let mut data_rows: Vec<DataRow> = vec![];
let mut pkeys: Vec<String> = vec![]; let mut pkeys: Vec<String> = vec![];
for row in rows.iter() { for row in rows.iter() {
let mut pkey_values: HashMap<String, Encodable> = HashMap::new(); let mut pkey_values: HashMap<String, Encodable> = HashMap::new();
@ -303,9 +265,9 @@ pub async fn get_data_page_get(
pkeys.push(pkey.clone()); pkeys.push(pkey.clone());
let mut row_data: Vec<Encodable> = vec![]; let mut row_data: Vec<Encodable> = vec![];
for field in fields.iter() { for field in fields.iter() {
row_data.push(field.get_value_encodable(row)?); row_data.push(field.field.get_value_encodable(row)?);
} }
data.push(DataRow { data_rows.push(DataRow {
pkey, pkey,
data: row_data, data: row_data,
}); });
@ -313,14 +275,14 @@ pub async fn get_data_page_get(
#[derive(Serialize)] #[derive(Serialize)]
struct ResponseBody { struct ResponseBody {
rows: Vec<DataRow>,
fields: Vec<FieldInfo>,
pkeys: Vec<String>, pkeys: Vec<String>,
data: Vec<DataRow>,
fields: Vec<Field>,
} }
Ok(Json(ResponseBody { Ok(Json(ResponseBody {
rows: data_rows,
fields, fields,
pkeys, pkeys,
data,
}) })
.into_response()) .into_response())
} }
@ -472,6 +434,7 @@ pub async fn update_value_page_post(
body.pkeys body.pkeys
.get(&pkey_attrs.first().unwrap().attname) .get(&pkey_attrs.first().unwrap().attname)
.unwrap() .unwrap()
.clone()
.bind_onto(body.value.bind_onto(query(&format!( .bind_onto(body.value.bind_onto(query(&format!(
r#"update {0}.{1} set {2} = $1 where {3} = $2"#, r#"update {0}.{1} set {2} = $1 where {3} = $2"#,
escape_identifier(&rel.regnamespace), escape_identifier(&rel.regnamespace),

View file

@ -1,3 +1,14 @@
use serde::Deserialize;
use uuid::Uuid;
pub mod bases; pub mod bases;
pub mod lens_insert;
pub mod lenses; pub mod lenses;
pub mod relations; pub mod relations;
#[derive(Deserialize)]
pub struct LensPagePath {
base_id: Uuid,
class_oid: u32,
lens_id: Uuid,
}

View file

@ -11,7 +11,6 @@
}, },
"dependencies": { "dependencies": {
"@deno/vite-plugin": "^1.0.5", "@deno/vite-plugin": "^1.0.5",
"@jsr/std__uuid": "^1.0.9",
"@sveltejs/vite-plugin-svelte": "^6.1.1", "@sveltejs/vite-plugin-svelte": "^6.1.1",
"svelte-language-server": "^0.17.19", "svelte-language-server": "^0.17.19",
"uuid": "^11.1.0", "uuid": "^11.1.0",

View file

@ -43,12 +43,6 @@ $table-border-color: #ccc;
text-align: left; text-align: left;
} }
&__header-actions {
border: none;
background: none;
padding: 0;
}
&__main { &__main {
grid-area: main; grid-area: main;
overflow-y: auto; overflow-y: auto;
@ -69,25 +63,32 @@ $table-border-color: #ccc;
&--insertable { &--insertable {
border-style: dashed; border-style: dashed;
} }
}
&__inserter {
align-items: stretch;
display: flex;
grid-area: inserter;
justify-items: flex-start;
margin-bottom: 2rem;
}
}
.lens-cell {
&__container {
align-items: center;
display: flex;
&--selected { &--selected {
.lens-table__cell-content {
outline: 3px solid #37f; outline: 3px solid #37f;
outline-offset: -2px; outline-offset: -2px;
} }
} }
}
&__cell-content { &__content {
flex: 1;
font-family: globals.$font-family-data; font-family: globals.$font-family-data;
&--null {
color: color.scale(#000, $lightness: 50%, $space: hsl);
font-style: oblique;
text-align: center;
padding: 0.5rem;
}
&--text { &--text {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -102,22 +103,60 @@ $table-border-color: #ccc;
white-space: nowrap; white-space: nowrap;
padding: 0.5rem; padding: 0.5rem;
} }
&--null {
color: color.scale(#000, $lightness: 50%, $space: hsl);
font-family: globals.$font-family-data;
font-style: oblique;
text-align: center;
padding: 0.5rem;
}
} }
&__inserter { &__notice {
grid-area: inserter; align-items: center;
margin-bottom: 2rem; color: color.scale(#000, $lightness: 50%, $space: hsl);
display: flex;
padding: 0 0.25rem;
svg path {
stroke: currentColor;
}
}
}
.lens-inserter {
&__rows {
.lens-table__cell { .lens-table__cell {
border: dashed 1px $table-border-color; border: dashed 1px $table-border-color;
border-left: none; border-left: none;
border-top: none; border-top: none;
&:last-child {
border-right: none;
}
} }
.lens-table__row:first-child .lens-table__cell { .lens-table__row:first-child .lens-table__cell {
border-top: dashed 1px $table-border-color; border-top: dashed 1px $table-border-color;
} }
} }
&__submit {
@include globals.reset-button;
align-items: center;
border: dashed 1px globals.$button-primary-background;
border-bottom-right-radius: globals.$border-radius-rounded-sm;
border-top-right-radius: globals.$border-radius-rounded-sm;
color: globals.$button-primary-background;
display: flex;
justify-content: center;
padding: 0 1rem;
svg path {
stroke: currentColor;
}
}
} }
.lens-editor { .lens-editor {
@ -128,7 +167,7 @@ $table-border-color: #ccc;
&__input { &__input {
@include globals.reset_input; @include globals.reset_input;
padding: 0.5rem; padding: 0.75rem 0.5rem;
font-family: globals.$font-family-data; font-family: globals.$font-family-data;
flex: 1; flex: 1;
} }

View file

@ -1 +0,0 @@
@jsr:registry=https://npm.jsr.io

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 768 B

View file

Before

Width:  |  Height:  |  Size: 894 B

After

Width:  |  Height:  |  Size: 894 B

View file

Before

Width:  |  Height:  |  Size: 517 B

After

Width:  |  Height:  |  Size: 517 B

View file

Before

Width:  |  Height:  |  Size: 660 B

After

Width:  |  Height:  |  Size: 660 B

View file

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 488 B

View file

Before

Width:  |  Height:  |  Size: 877 B

After

Width:  |  Height:  |  Size: 877 B

View file

Before

Width:  |  Height:  |  Size: 674 B

After

Width:  |  Height:  |  Size: 674 B

View file

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 490 B

View file

Before

Width:  |  Height:  |  Size: 847 B

After

Width:  |  Height:  |  Size: 847 B

View file

Before

Width:  |  Height:  |  Size: 550 B

After

Width:  |  Height:  |  Size: 550 B

View file

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

View file

Before

Width:  |  Height:  |  Size: 988 B

After

Width:  |  Height:  |  Size: 988 B

View file

Before

Width:  |  Height:  |  Size: 1,023 B

After

Width:  |  Height:  |  Size: 1,023 B

View file

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 566 B

View file

Before

Width:  |  Height:  |  Size: 550 B

After

Width:  |  Height:  |  Size: 550 B

View file

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 566 B

View file

Before

Width:  |  Height:  |  Size: 560 B

After

Width:  |  Height:  |  Size: 560 B

View file

Before

Width:  |  Height:  |  Size: 556 B

After

Width:  |  Height:  |  Size: 556 B

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 670 B

After

Width:  |  Height:  |  Size: 670 B

View file

Before

Width:  |  Height:  |  Size: 988 B

After

Width:  |  Height:  |  Size: 988 B

View file

Before

Width:  |  Height:  |  Size: 1,011 B

After

Width:  |  Height:  |  Size: 1,011 B

View file

Before

Width:  |  Height:  |  Size: 560 B

After

Width:  |  Height:  |  Size: 560 B

View file

Before

Width:  |  Height:  |  Size: 838 B

After

Width:  |  Height:  |  Size: 838 B

View file

Before

Width:  |  Height:  |  Size: 926 B

After

Width:  |  Height:  |  Size: 926 B

View file

Before

Width:  |  Height:  |  Size: 883 B

After

Width:  |  Height:  |  Size: 883 B

View file

Before

Width:  |  Height:  |  Size: 597 B

After

Width:  |  Height:  |  Size: 597 B

View file

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View file

Before

Width:  |  Height:  |  Size: 658 B

After

Width:  |  Height:  |  Size: 658 B

View file

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 610 B

View file

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 600 B

View file

Before

Width:  |  Height:  |  Size: 602 B

After

Width:  |  Height:  |  Size: 602 B

View file

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 600 B

View file

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 592 B

View file

Before

Width:  |  Height:  |  Size: 689 B

After

Width:  |  Height:  |  Size: 689 B

View file

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 486 B

View file

Before

Width:  |  Height:  |  Size: 882 B

After

Width:  |  Height:  |  Size: 882 B

View file

Before

Width:  |  Height:  |  Size: 700 B

After

Width:  |  Height:  |  Size: 700 B

View file

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 488 B

View file

Before

Width:  |  Height:  |  Size: 853 B

After

Width:  |  Height:  |  Size: 853 B

View file

Before

Width:  |  Height:  |  Size: 566 B

After

Width:  |  Height:  |  Size: 566 B

View file

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

View file

Before

Width:  |  Height:  |  Size: 698 B

After

Width:  |  Height:  |  Size: 698 B

View file

Before

Width:  |  Height:  |  Size: 691 B

After

Width:  |  Height:  |  Size: 691 B

View file

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 697 B

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Before

Width:  |  Height:  |  Size: 975 B

After

Width:  |  Height:  |  Size: 975 B

View file

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 954 B

After

Width:  |  Height:  |  Size: 954 B

View file

Before

Width:  |  Height:  |  Size: 644 B

After

Width:  |  Height:  |  Size: 644 B

View file

Before

Width:  |  Height:  |  Size: 1,011 B

After

Width:  |  Height:  |  Size: 1,011 B

View file

Before

Width:  |  Height:  |  Size: 454 B

After

Width:  |  Height:  |  Size: 454 B

View file

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View file

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

View file

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 589 B

View file

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 594 B

View file

Before

Width:  |  Height:  |  Size: 799 B

After

Width:  |  Height:  |  Size: 799 B

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 571 B

After

Width:  |  Height:  |  Size: 571 B

View file

Before

Width:  |  Height:  |  Size: 740 B

After

Width:  |  Height:  |  Size: 740 B

View file

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

View file

Before

Width:  |  Height:  |  Size: 951 B

After

Width:  |  Height:  |  Size: 951 B

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 991 B

After

Width:  |  Height:  |  Size: 991 B

View file

Before

Width:  |  Height:  |  Size: 966 B

After

Width:  |  Height:  |  Size: 966 B

View file

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 622 B

View file

Before

Width:  |  Height:  |  Size: 536 B

After

Width:  |  Height:  |  Size: 536 B

View file

Before

Width:  |  Height:  |  Size: 975 B

After

Width:  |  Height:  |  Size: 975 B

View file

Before

Width:  |  Height:  |  Size: 622 B

After

Width:  |  Height:  |  Size: 622 B

View file

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

View file

Before

Width:  |  Height:  |  Size: 620 B

After

Width:  |  Height:  |  Size: 620 B

View file

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 611 B

View file

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 408 B

View file

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 743 B

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Some files were not shown because too many files have changed in this diff Show more